Merge "Remove almost-all android::Singleton users"
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 12780a8..7f33cb5 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -18,7 +18,7 @@
package com.android.commands.am;
-import static android.app.ActivityManager.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.app.ActivityManager.RESIZE_MODE_SYSTEM;
import static android.app.ActivityManager.RESIZE_MODE_USER;
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 2960cdc..ab781bb 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -25,7 +25,6 @@
import android.accounts.IAccountManager;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
-import android.app.IActivityManager;
import android.app.PackageInstallObserver;
import android.content.ComponentName;
import android.content.Context;
@@ -34,30 +33,25 @@
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.ApplicationInfo;
-import android.content.pm.FeatureInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageInstaller;
import android.content.pm.IPackageManager;
-import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionInfo;
import android.content.pm.PackageInstaller.SessionParams;
-import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
-import android.content.pm.ParceledListSlice;
-import android.content.pm.PermissionGroupInfo;
-import android.content.pm.PermissionInfo;
import android.content.pm.UserInfo;
import android.content.pm.VerificationParams;
-import android.content.res.AssetManager;
-import android.content.res.Resources;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.os.IUserManager;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -80,11 +74,6 @@
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.WeakHashMap;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
@@ -96,9 +85,6 @@
IUserManager mUm;
IAccountManager mAm;
- private WeakHashMap<String, Resources> mResourceCache
- = new WeakHashMap<String, Resources>();
-
private String[] mArgs;
private int mNextArg;
private String mCurArgData;
@@ -275,10 +261,10 @@
if (args.length == 1) {
if (args[0].equalsIgnoreCase("-l")) {
validCommand = true;
- return runListPackages(false);
- } else if (args[0].equalsIgnoreCase("-lf")){
+ return runShellCommand("package", new String[] { "list", "package" });
+ } else if (args[0].equalsIgnoreCase("-lf")) {
validCommand = true;
- return runListPackages(true);
+ return runShellCommand("package", new String[] { "list", "package", "-f" });
}
} else if (args.length == 2) {
if (args[0].equalsIgnoreCase("-p")) {
@@ -297,6 +283,22 @@
}
}
+ private int runShellCommand(String serviceName, String[] args) {
+ final HandlerThread handlerThread = new HandlerThread("results");
+ handlerThread.start();
+ try {
+ ServiceManager.getService(serviceName).shellCommand(
+ FileDescriptor.in, FileDescriptor.out, FileDescriptor.err,
+ args, new ResultReceiver(new Handler(handlerThread.getLooper())));
+ return 0;
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ } finally {
+ handlerThread.quitSafely();
+ }
+ return -1;
+ }
+
/**
* Execute the list sub-command.
*
@@ -308,462 +310,11 @@
* pm list instrumentation
*/
private int runList() {
- String type = nextArg();
- if (type == null) {
- System.err.println("Error: didn't specify type of data to list");
- return 1;
+ final String type = nextArg();
+ if ("users".equals(type)) {
+ return runShellCommand("user", new String[] { "list" });
}
- if ("package".equals(type) || "packages".equals(type)) {
- return runListPackages(false);
- } else if ("permission-groups".equals(type)) {
- return runListPermissionGroups();
- } else if ("permissions".equals(type)) {
- return runListPermissions();
- } else if ("features".equals(type)) {
- return runListFeatures();
- } else if ("libraries".equals(type)) {
- return runListLibraries();
- } else if ("instrumentation".equals(type)) {
- return runListInstrumentation();
- } else if ("users".equals(type)) {
- return runListUsers();
- } else {
- System.err.println("Error: unknown list type '" + type + "'");
- return 1;
- }
- }
-
- /**
- * Lists all the installed packages.
- */
- private int runListPackages(boolean showApplicationPackage) {
- int getFlags = 0;
- boolean listDisabled = false, listEnabled = false;
- boolean listSystem = false, listThirdParty = false;
- boolean listInstaller = false;
- int userId = UserHandle.USER_SYSTEM;
- try {
- String opt;
- while ((opt=nextOption()) != null) {
- if (opt.equals("-l")) {
- // old compat
- } else if (opt.equals("-lf")) {
- showApplicationPackage = true;
- } else if (opt.equals("-f")) {
- showApplicationPackage = true;
- } else if (opt.equals("-d")) {
- listDisabled = true;
- } else if (opt.equals("-e")) {
- listEnabled = true;
- } else if (opt.equals("-s")) {
- listSystem = true;
- } else if (opt.equals("-3")) {
- listThirdParty = true;
- } else if (opt.equals("-i")) {
- listInstaller = true;
- } else if (opt.equals("--user")) {
- userId = Integer.parseInt(nextArg());
- } else if (opt.equals("-u")) {
- getFlags |= PackageManager.GET_UNINSTALLED_PACKAGES;
- } else {
- System.err.println("Error: Unknown option: " + opt);
- return 1;
- }
- }
- } catch (RuntimeException ex) {
- System.err.println("Error: " + ex.toString());
- return 1;
- }
-
- String filter = nextArg();
-
- try {
- final List<PackageInfo> packages = getInstalledPackages(mPm, getFlags, userId);
-
- int count = packages.size();
- for (int p = 0 ; p < count ; p++) {
- PackageInfo info = packages.get(p);
- if (filter != null && !info.packageName.contains(filter)) {
- continue;
- }
- final boolean isSystem =
- (info.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0;
- if ((!listDisabled || !info.applicationInfo.enabled) &&
- (!listEnabled || info.applicationInfo.enabled) &&
- (!listSystem || isSystem) &&
- (!listThirdParty || !isSystem)) {
- System.out.print("package:");
- if (showApplicationPackage) {
- System.out.print(info.applicationInfo.sourceDir);
- System.out.print("=");
- }
- System.out.print(info.packageName);
- if (listInstaller) {
- System.out.print(" installer=");
- System.out.print(mPm.getInstallerPackageName(info.packageName));
- }
- System.out.println();
- }
- }
- return 0;
- } catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(PM_NOT_RUNNING_ERR);
- return 1;
- }
- }
-
- @SuppressWarnings("unchecked")
- private List<PackageInfo> getInstalledPackages(IPackageManager pm, int flags, int userId)
- throws RemoteException {
- ParceledListSlice<PackageInfo> slice = pm.getInstalledPackages(flags, userId);
- return slice.getList();
- }
-
- /**
- * Lists all of the features supported by the current device.
- *
- * pm list features
- */
- private int runListFeatures() {
- try {
- List<FeatureInfo> list = new ArrayList<FeatureInfo>();
- FeatureInfo[] rawList = mPm.getSystemAvailableFeatures();
- for (int i=0; i<rawList.length; i++) {
- list.add(rawList[i]);
- }
-
-
- // Sort by name
- Collections.sort(list, new Comparator<FeatureInfo>() {
- public int compare(FeatureInfo o1, FeatureInfo o2) {
- if (o1.name == o2.name) return 0;
- if (o1.name == null) return -1;
- if (o2.name == null) return 1;
- return o1.name.compareTo(o2.name);
- }
- });
-
- int count = (list != null) ? list.size() : 0;
- for (int p = 0; p < count; p++) {
- FeatureInfo fi = list.get(p);
- System.out.print("feature:");
- if (fi.name != null) System.out.println(fi.name);
- else System.out.println("reqGlEsVersion=0x"
- + Integer.toHexString(fi.reqGlEsVersion));
- }
- return 0;
- } catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(PM_NOT_RUNNING_ERR);
- return 1;
- }
- }
-
- /**
- * Lists all of the libraries supported by the current device.
- *
- * pm list libraries
- */
- private int runListLibraries() {
- try {
- List<String> list = new ArrayList<String>();
- String[] rawList = mPm.getSystemSharedLibraryNames();
- for (int i=0; i<rawList.length; i++) {
- list.add(rawList[i]);
- }
-
-
- // Sort by name
- Collections.sort(list, new Comparator<String>() {
- public int compare(String o1, String o2) {
- if (o1 == o2) return 0;
- if (o1 == null) return -1;
- if (o2 == null) return 1;
- return o1.compareTo(o2);
- }
- });
-
- int count = (list != null) ? list.size() : 0;
- for (int p = 0; p < count; p++) {
- String lib = list.get(p);
- System.out.print("library:");
- System.out.println(lib);
- }
- return 0;
- } catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(PM_NOT_RUNNING_ERR);
- return 1;
- }
- }
-
- /**
- * Lists all of the installed instrumentation, or all for a given package
- *
- * pm list instrumentation [package] [-f]
- */
- private int runListInstrumentation() {
- int flags = 0; // flags != 0 is only used to request meta-data
- boolean showPackage = false;
- String targetPackage = null;
-
- try {
- String opt;
- while ((opt=nextArg()) != null) {
- if (opt.equals("-f")) {
- showPackage = true;
- } else if (opt.charAt(0) != '-') {
- targetPackage = opt;
- } else {
- System.err.println("Error: Unknown option: " + opt);
- return 1;
- }
- }
- } catch (RuntimeException ex) {
- System.err.println("Error: " + ex.toString());
- return 1;
- }
-
- try {
- List<InstrumentationInfo> list = mPm.queryInstrumentation(targetPackage, flags);
-
- // Sort by target package
- Collections.sort(list, new Comparator<InstrumentationInfo>() {
- public int compare(InstrumentationInfo o1, InstrumentationInfo o2) {
- return o1.targetPackage.compareTo(o2.targetPackage);
- }
- });
-
- int count = (list != null) ? list.size() : 0;
- for (int p = 0; p < count; p++) {
- InstrumentationInfo ii = list.get(p);
- System.out.print("instrumentation:");
- if (showPackage) {
- System.out.print(ii.sourceDir);
- System.out.print("=");
- }
- ComponentName cn = new ComponentName(ii.packageName, ii.name);
- System.out.print(cn.flattenToShortString());
- System.out.print(" (target=");
- System.out.print(ii.targetPackage);
- System.out.println(")");
- }
- return 0;
- } catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(PM_NOT_RUNNING_ERR);
- return 1;
- }
- }
-
- /**
- * Lists all the known permission groups.
- */
- private int runListPermissionGroups() {
- try {
- List<PermissionGroupInfo> pgs = mPm.getAllPermissionGroups(0);
-
- int count = pgs.size();
- for (int p = 0 ; p < count ; p++) {
- PermissionGroupInfo pgi = pgs.get(p);
- System.out.print("permission group:");
- System.out.println(pgi.name);
- }
- return 0;
- } catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(PM_NOT_RUNNING_ERR);
- return 1;
- }
- }
-
- private String loadText(PackageItemInfo pii, int res, CharSequence nonLocalized) {
- if (nonLocalized != null) {
- return nonLocalized.toString();
- }
- if (res != 0) {
- Resources r = getResources(pii);
- if (r != null) {
- try {
- return r.getString(res);
- } catch (Resources.NotFoundException e) {
- }
- }
- }
- return null;
- }
-
- /**
- * Lists all the permissions in a group.
- */
- private int runListPermissions() {
- try {
- boolean labels = false;
- boolean groups = false;
- boolean userOnly = false;
- boolean summary = false;
- boolean dangerousOnly = false;
- String opt;
- while ((opt=nextOption()) != null) {
- if (opt.equals("-f")) {
- labels = true;
- } else if (opt.equals("-g")) {
- groups = true;
- } else if (opt.equals("-s")) {
- groups = true;
- labels = true;
- summary = true;
- } else if (opt.equals("-u")) {
- userOnly = true;
- } else if (opt.equals("-d")) {
- dangerousOnly = true;
- } else {
- System.err.println("Error: Unknown option: " + opt);
- return 1;
- }
- }
-
- String grp = nextArg();
- ArrayList<String> groupList = new ArrayList<String>();
- if (groups) {
- List<PermissionGroupInfo> infos =
- mPm.getAllPermissionGroups(0);
- for (int i=0; i<infos.size(); i++) {
- groupList.add(infos.get(i).name);
- }
- groupList.add(null);
- } else {
- groupList.add(grp);
- }
-
- if (dangerousOnly) {
- System.out.println("Dangerous Permissions:");
- System.out.println("");
- doListPermissions(groupList, groups, labels, summary,
- PermissionInfo.PROTECTION_DANGEROUS,
- PermissionInfo.PROTECTION_DANGEROUS);
- if (userOnly) {
- System.out.println("Normal Permissions:");
- System.out.println("");
- doListPermissions(groupList, groups, labels, summary,
- PermissionInfo.PROTECTION_NORMAL,
- PermissionInfo.PROTECTION_NORMAL);
- }
- } else if (userOnly) {
- System.out.println("Dangerous and Normal Permissions:");
- System.out.println("");
- doListPermissions(groupList, groups, labels, summary,
- PermissionInfo.PROTECTION_NORMAL,
- PermissionInfo.PROTECTION_DANGEROUS);
- } else {
- System.out.println("All Permissions:");
- System.out.println("");
- doListPermissions(groupList, groups, labels, summary,
- -10000, 10000);
- }
- return 0;
- } catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(PM_NOT_RUNNING_ERR);
- return 1;
- }
- }
-
- private void doListPermissions(ArrayList<String> groupList,
- boolean groups, boolean labels, boolean summary,
- int startProtectionLevel, int endProtectionLevel)
- throws RemoteException {
- for (int i=0; i<groupList.size(); i++) {
- String groupName = groupList.get(i);
- String prefix = "";
- if (groups) {
- if (i > 0) System.out.println("");
- if (groupName != null) {
- PermissionGroupInfo pgi = mPm.getPermissionGroupInfo(
- groupName, 0);
- if (summary) {
- Resources res = getResources(pgi);
- if (res != null) {
- System.out.print(loadText(pgi, pgi.labelRes,
- pgi.nonLocalizedLabel) + ": ");
- } else {
- System.out.print(pgi.name + ": ");
-
- }
- } else {
- System.out.println((labels ? "+ " : "")
- + "group:" + pgi.name);
- if (labels) {
- System.out.println(" package:" + pgi.packageName);
- Resources res = getResources(pgi);
- if (res != null) {
- System.out.println(" label:"
- + loadText(pgi, pgi.labelRes,
- pgi.nonLocalizedLabel));
- System.out.println(" description:"
- + loadText(pgi, pgi.descriptionRes,
- pgi.nonLocalizedDescription));
- }
- }
- }
- } else {
- System.out.println(((labels && !summary)
- ? "+ " : "") + "ungrouped:");
- }
- prefix = " ";
- }
- List<PermissionInfo> ps = mPm.queryPermissionsByGroup(
- groupList.get(i), 0);
- int count = ps.size();
- boolean first = true;
- for (int p = 0 ; p < count ; p++) {
- PermissionInfo pi = ps.get(p);
- if (groups && groupName == null && pi.group != null) {
- continue;
- }
- final int base = pi.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
- if (base < startProtectionLevel
- || base > endProtectionLevel) {
- continue;
- }
- if (summary) {
- if (first) {
- first = false;
- } else {
- System.out.print(", ");
- }
- Resources res = getResources(pi);
- if (res != null) {
- System.out.print(loadText(pi, pi.labelRes,
- pi.nonLocalizedLabel));
- } else {
- System.out.print(pi.name);
- }
- } else {
- System.out.println(prefix + (labels ? "+ " : "")
- + "permission:" + pi.name);
- if (labels) {
- System.out.println(prefix + " package:" + pi.packageName);
- Resources res = getResources(pi);
- if (res != null) {
- System.out.println(prefix + " label:"
- + loadText(pi, pi.labelRes,
- pi.nonLocalizedLabel));
- System.out.println(prefix + " description:"
- + loadText(pi, pi.descriptionRes,
- pi.nonLocalizedDescription));
- }
- System.out.println(prefix + " protectionLevel:"
- + PermissionInfo.protectionToString(pi.protectionLevel));
- }
- }
- }
-
- if (summary) {
- System.out.println("");
- }
- }
+ return runShellCommand("package", mArgs);
}
private int runPath() {
@@ -1467,29 +1018,6 @@
}
}
- public int runListUsers() {
- try {
- IActivityManager am = ActivityManagerNative.getDefault();
-
- List<UserInfo> users = mUm.getUsers(false);
- if (users == null) {
- System.err.println("Error: couldn't get users");
- return 1;
- } else {
- System.out.println("Users:");
- for (int i = 0; i < users.size(); i++) {
- String running = am.isUserRunning(users.get(i).id, false) ? " running" : "";
- System.out.println("\t" + users.get(i).toString() + running);
- }
- return 0;
- }
- } catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(PM_NOT_RUNNING_ERR);
- return 1;
- }
- }
-
public int runGetMaxUsers() {
System.out.println("Maximum supported users: " + UserManager.getMaxSupportedUsers());
return 0;
@@ -1997,24 +1525,6 @@
return 1;
}
- private Resources getResources(PackageItemInfo pii) {
- Resources res = mResourceCache.get(pii.packageName);
- if (res != null) return res;
-
- try {
- ApplicationInfo ai = mPm.getApplicationInfo(pii.packageName, 0, 0);
- AssetManager am = new AssetManager();
- am.addAssetPath(ai.publicSourceDir);
- res = new Resources(am, null, null);
- mResourceCache.put(pii.packageName, res);
- return res;
- } catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(PM_NOT_RUNNING_ERR);
- return null;
- }
- }
-
private static String checkAbiArgument(String abi) {
if (TextUtils.isEmpty(abi)) {
throw new IllegalArgumentException("Missing ABI argument");
@@ -2110,14 +1620,7 @@
}
private static int showUsage() {
- System.err.println("usage: pm list packages [-f] [-d] [-e] [-s] [-3] [-i] [-u] [--user USER_ID] [FILTER]");
- System.err.println(" pm list permission-groups");
- System.err.println(" pm list permissions [-g] [-f] [-d] [-u] [GROUP]");
- System.err.println(" pm list instrumentation [-f] [TARGET-PACKAGE]");
- System.err.println(" pm list features");
- System.err.println(" pm list libraries");
- System.err.println(" pm list users");
- System.err.println(" pm path [--user USER_ID] PACKAGE");
+ System.err.println("usage: pm path [--user USER_ID] PACKAGE");
System.err.println(" pm dump PACKAGE");
System.err.println(" pm install [-lrtsfd] [-i PACKAGE] [--user USER_ID] [PATH]");
System.err.println(" pm install-create [-lrtsfdp] [-i PACKAGE] [-S BYTES]");
@@ -2151,34 +1654,8 @@
System.err.println(" pm remove-user USER_ID");
System.err.println(" pm get-max-users");
System.err.println("");
- System.err.println("pm list packages: prints all packages, optionally only");
- System.err.println(" those whose package name contains the text in FILTER. Options:");
- System.err.println(" -f: see their associated file.");
- System.err.println(" -d: filter to only show disbled packages.");
- System.err.println(" -e: filter to only show enabled packages.");
- System.err.println(" -s: filter to only show system packages.");
- System.err.println(" -3: filter to only show third party packages.");
- System.err.println(" -i: see the installer for the packages.");
- System.err.println(" -u: also include uninstalled packages.");
- System.err.println("");
- System.err.println("pm list permission-groups: prints all known permission groups.");
- System.err.println("");
- System.err.println("pm list permissions: prints all known permissions, optionally only");
- System.err.println(" those in GROUP. Options:");
- System.err.println(" -g: organize by group.");
- System.err.println(" -f: print all information.");
- System.err.println(" -s: short summary.");
- System.err.println(" -d: only list dangerous permissions.");
- System.err.println(" -u: list only the permissions users will see.");
- System.err.println("");
- System.err.println("pm list instrumentation: use to list all test packages; optionally");
- System.err.println(" supply <TARGET-PACKAGE> to list the test packages for a particular");
- System.err.println(" application. Options:");
- System.err.println(" -f: list the .apk file for the test package.");
- System.err.println("");
- System.err.println("pm list features: prints all features of the system.");
- System.err.println("");
- System.err.println("pm list users: prints all users on the system.");
+ System.err.println("NOTE: 'pm list' commands have moved! Run 'adb shell cmd package'");
+ System.err.println(" to display the new commands.");
System.err.println("");
System.err.println("pm path: print the path to the .apk of the given PACKAGE.");
System.err.println("");
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 2f0849f..472d97f 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2728,8 +2728,8 @@
/**
* Called to move the window and its activity/task to a different stack container.
* For example, a window can move between
- * {@link android.app.ActivityManager#FULLSCREEN_WORKSPACE_STACK_ID} stack and
- * {@link android.app.ActivityManager#FREEFORM_WORKSPACE_STACK_ID} stack.
+ * {@link android.app.ActivityManager.StackId#FULLSCREEN_WORKSPACE_STACK_ID} stack and
+ * {@link android.app.ActivityManager.StackId#FREEFORM_WORKSPACE_STACK_ID} stack.
*
* @param stackId stack Id to change to.
* @hide
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index fce1b2e..b809baa 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -73,7 +73,6 @@
*/
public class ActivityManager {
private static String TAG = "ActivityManager";
- private static boolean localLOGV = false;
private static int gMaxRecentTasks = -1;
@@ -397,60 +396,112 @@
*/
public static final int COMPAT_MODE_TOGGLE = 2;
- /**
- * Invalid stack ID.
- * @hide
- */
- public static final int INVALID_STACK_ID = -1;
+ /** @hide */
+ public static class StackId {
+ /** Invalid stack ID. */
+ public static final int INVALID_STACK_ID = -1;
- /**
- * First static stack ID.
- * @hide
- */
- public static final int FIRST_STATIC_STACK_ID = 0;
+ /** First static stack ID. */
+ public static final int FIRST_STATIC_STACK_ID = 0;
- /**
- * Home activity stack ID.
- * @hide
- */
- public static final int HOME_STACK_ID = FIRST_STATIC_STACK_ID;
+ /** Home activity stack ID. */
+ public static final int HOME_STACK_ID = FIRST_STATIC_STACK_ID;
- /**
- * ID of stack where fullscreen activities are normally launched into.
- * @hide
- */
- public static final int FULLSCREEN_WORKSPACE_STACK_ID = 1;
+ /** ID of stack where fullscreen activities are normally launched into. */
+ public static final int FULLSCREEN_WORKSPACE_STACK_ID = 1;
- /**
- * ID of stack where freeform/resized activities are normally launched into.
- * @hide
- */
- public static final int FREEFORM_WORKSPACE_STACK_ID = FULLSCREEN_WORKSPACE_STACK_ID + 1;
+ /** ID of stack where freeform/resized activities are normally launched into. */
+ public static final int FREEFORM_WORKSPACE_STACK_ID = FULLSCREEN_WORKSPACE_STACK_ID + 1;
- /**
- * ID of stack that occupies a dedicated region of the screen.
- * @hide
- */
- public static final int DOCKED_STACK_ID = FREEFORM_WORKSPACE_STACK_ID + 1;
+ /** ID of stack that occupies a dedicated region of the screen. */
+ public static final int DOCKED_STACK_ID = FREEFORM_WORKSPACE_STACK_ID + 1;
- /**
- * ID of stack that always on top (always visible) when it exist.
- * Mainly used for this in Picture-in-Picture mode.
- * @hide
- */
- public static final int PINNED_STACK_ID = DOCKED_STACK_ID + 1;
+ /** ID of stack that always on top (always visible) when it exist. */
+ public static final int PINNED_STACK_ID = DOCKED_STACK_ID + 1;
- /**
- * Last static stack stack ID.
- * @hide
- */
- public static final int LAST_STATIC_STACK_ID = PINNED_STACK_ID;
+ /** Last static stack stack ID. */
+ public static final int LAST_STATIC_STACK_ID = PINNED_STACK_ID;
- /**
- * Start of ID range used by stacks that are created dynamically.
- * @hide
- */
- public static final int FIRST_DYNAMIC_STACK_ID = LAST_STATIC_STACK_ID + 1;
+ /** Start of ID range used by stacks that are created dynamically. */
+ public static final int FIRST_DYNAMIC_STACK_ID = LAST_STATIC_STACK_ID + 1;
+
+ public static boolean isStaticStack(int stackId) {
+ return stackId >= FIRST_STATIC_STACK_ID && stackId <= LAST_STATIC_STACK_ID;
+ }
+
+ /**
+ * Returns true if the activities contained in the input stack display a shadow around
+ * their border.
+ */
+ public static boolean hasWindowShadow(int stackId) {
+ return stackId == FREEFORM_WORKSPACE_STACK_ID || stackId == PINNED_STACK_ID;
+ }
+
+ /**
+ * Returns true if the activities contained in the input stack display a decor view.
+ */
+ public static boolean hasWindowDecor(int stackId) {
+ return stackId == FREEFORM_WORKSPACE_STACK_ID;
+ }
+
+ /**
+ * Returns true if the tasks contained in the stack can be resized independently of the
+ * stack.
+ */
+ public static boolean isTaskResizeAllowed(int stackId) {
+ return stackId == FREEFORM_WORKSPACE_STACK_ID;
+ }
+
+ /**
+ * Returns true if the task bounds should persist across power cycles.
+ */
+ public static boolean persistTaskBounds(int stackId) {
+ return isStaticStack(stackId) &&
+ stackId != DOCKED_STACK_ID && stackId != PINNED_STACK_ID;
+ }
+
+ /**
+ * Returns true if dynamic stacks are allowed to be visible behind the input stack.
+ */
+ public static boolean isDynamicStacksVisibleBehindAllowed(int stackId) {
+ return stackId == PINNED_STACK_ID;
+ }
+
+ /**
+ * Returns true if we try to maintain focus in the current stack when the top activity
+ * finishes.
+ */
+ public static boolean keepFocusInStackIfPossible(int stackId) {
+ return stackId == FREEFORM_WORKSPACE_STACK_ID
+ || stackId == DOCKED_STACK_ID || stackId == PINNED_STACK_ID;
+ }
+
+ /**
+ * Returns true if Stack size is affected by the docked stack changing size.
+ */
+ public static boolean isResizeableByDockedStack(int stackId) {
+ return isStaticStack(stackId) &&
+ stackId != DOCKED_STACK_ID && stackId != PINNED_STACK_ID;
+ }
+
+ /**
+ * Returns true if the size of tasks in the input stack are affected by the docked stack
+ * changing size.
+ */
+ public static boolean isTaskResizeableByDockedStack(int stackId) {
+ return isStaticStack(stackId) && stackId != FREEFORM_WORKSPACE_STACK_ID
+ && stackId != DOCKED_STACK_ID && stackId != PINNED_STACK_ID;
+ }
+
+ /**
+ * Returns true if the windows of tasks being moved to this stack should be preserved so
+ * there isn't a display gap.
+ */
+ public static boolean preserveWindowOnTaskMove(int stackId) {
+ return stackId == FULLSCREEN_WORKSPACE_STACK_ID
+ || stackId == DOCKED_STACK_ID || stackId == PINNED_STACK_ID;
+ }
+ }
/**
* Input parameter to {@link android.app.IActivityManager#moveTaskToDockedStack} which
@@ -1016,6 +1067,12 @@
*/
public int numActivities;
+ /**
+ * The bounds of the task.
+ * @hide
+ */
+ public Rect bounds;
+
public RecentTaskInfo() {
}
@@ -1053,6 +1110,12 @@
ComponentName.writeToParcel(baseActivity, dest);
ComponentName.writeToParcel(topActivity, dest);
dest.writeInt(numActivities);
+ if (bounds != null) {
+ dest.writeInt(1);
+ bounds.writeToParcel(dest, 0);
+ } else {
+ dest.writeInt(0);
+ }
}
public void readFromParcel(Parcel source) {
@@ -1073,6 +1136,8 @@
baseActivity = ComponentName.readFromParcel(source);
topActivity = ComponentName.readFromParcel(source);
numActivities = source.readInt();
+ bounds = source.readInt() > 0 ?
+ Rect.CREATOR.createFromParcel(source) : null;
}
public static final Creator<RecentTaskInfo> CREATOR
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 09c0a6e..77a9795 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -653,7 +653,7 @@
null, //WRITE_SETTINGS
UserManager.DISALLOW_CREATE_WINDOWS, //SYSTEM_ALERT_WINDOW
null, //ACCESS_NOTIFICATIONS
- null, //CAMERA
+ UserManager.DISALLOW_CAMERA, //CAMERA
UserManager.DISALLOW_RECORD_AUDIO, //RECORD_AUDIO
null, //PLAY_AUDIO
null, //READ_CLIPBOARD
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 9e9d949..0fdf3d3 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2271,6 +2271,8 @@
* on the device, for this user. After setting this, no applications running as this user
* will be able to access any cameras on the device.
*
+ * <p>If the caller is device owner, then the restriction will be applied to all users.
+ *
* <p>The calling device admin must have requested
* {@link DeviceAdminInfo#USES_POLICY_DISABLE_CAMERA} to be able to call
* this method; if it has not, a security exception will be thrown.
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 27ecf9f..c57fc89 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1125,7 +1125,7 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
- * The device has professional audio level of functionality, performance, and acoustics.
+ * The device has professional audio level of functionality and performance.
*/
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_AUDIO_PRO = "android.hardware.audio.pro";
diff --git a/core/java/android/os/BatteryManager.java b/core/java/android/os/BatteryManager.java
index 1f3e9a7..56cb250 100644
--- a/core/java/android/os/BatteryManager.java
+++ b/core/java/android/os/BatteryManager.java
@@ -108,6 +108,13 @@
*/
public static final String EXTRA_MAX_CHARGING_CURRENT = "max_charging_current";
+ /**
+ * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}:
+ * Int value set to the maximum charging voltage supported by the charger in micro volts.
+ * {@hide}
+ */
+ public static final String EXTRA_MAX_CHARGING_VOLTAGE = "max_charging_voltage";
+
// values for "status" field in the ACTION_BATTERY_CHANGED Intent
public static final int BATTERY_STATUS_UNKNOWN = 1;
public static final int BATTERY_STATUS_CHARGING = 2;
diff --git a/core/java/android/os/BatteryProperties.java b/core/java/android/os/BatteryProperties.java
index 29e868c..c3e0f24 100644
--- a/core/java/android/os/BatteryProperties.java
+++ b/core/java/android/os/BatteryProperties.java
@@ -23,6 +23,7 @@
public boolean chargerUsbOnline;
public boolean chargerWirelessOnline;
public int maxChargingCurrent;
+ public int maxChargingVoltage;
public int batteryStatus;
public int batteryHealth;
public boolean batteryPresent;
@@ -39,6 +40,7 @@
chargerUsbOnline = other.chargerUsbOnline;
chargerWirelessOnline = other.chargerWirelessOnline;
maxChargingCurrent = other.maxChargingCurrent;
+ maxChargingVoltage = other.maxChargingVoltage;
batteryStatus = other.batteryStatus;
batteryHealth = other.batteryHealth;
batteryPresent = other.batteryPresent;
@@ -58,6 +60,7 @@
chargerUsbOnline = p.readInt() == 1 ? true : false;
chargerWirelessOnline = p.readInt() == 1 ? true : false;
maxChargingCurrent = p.readInt();
+ maxChargingVoltage = p.readInt();
batteryStatus = p.readInt();
batteryHealth = p.readInt();
batteryPresent = p.readInt() == 1 ? true : false;
@@ -72,6 +75,7 @@
p.writeInt(chargerUsbOnline ? 1 : 0);
p.writeInt(chargerWirelessOnline ? 1 : 0);
p.writeInt(maxChargingCurrent);
+ p.writeInt(maxChargingVoltage);
p.writeInt(batteryStatus);
p.writeInt(batteryHealth);
p.writeInt(batteryPresent ? 1 : 0);
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 2e31ab6..e892349 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -487,6 +487,16 @@
public static final String DISALLOW_RECORD_AUDIO = "no_record_audio";
/**
+ * Specifies if a user is not allowed to use the camera.
+ *
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+ * @see #getUserRestrictions()
+ * @hide
+ */
+ public static final String DISALLOW_CAMERA = "no_camera";
+
+ /**
* Allows apps in the parent profile to handle web links from the managed profile.
*
* This user restriction has an effect only in a managed profile.
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index f18b35d..c01c6df 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -201,6 +201,11 @@
*/
void setScreenCaptureDisabled(int userId, boolean disabled);
+ /**
+ * Cancels the window transitions for the given task.
+ */
+ void cancelTaskWindowTransition(int taskId);
+
// These can only be called with the SET_ORIENTATION permission.
/**
* Update the current screen rotation based on the current state of
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 017364a..1be2f95 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -96,6 +96,23 @@
out Rect outOutsets, out Configuration outConfig, out Surface outSurface);
/**
+ * Position a window relative to it's parent (attached) window without triggering
+ * a full relayout. This action may be deferred until a given frame number
+ * for the parent window appears. This allows for synchronizing movement of a child
+ * to repainting the contents of the parent.
+ *
+ * @param window The window being modified. Must be attached to a parent window
+ * or this call will fail.
+ * @param x The new x position
+ * @param y The new y position
+ * @param deferTransactionUntilFrame Frame number from our parent (attached) to
+ * defer this action until.
+ * @param outFrame Rect in which is placed the new position/size on screen.
+ */
+ void repositionChild(IWindow childWindow, int x, int y, long deferTransactionUntilFrame,
+ out Rect outFrame);
+
+ /**
* If a call to relayout() asked to have the surface destroy deferred,
* it must call this once it is okay to destroy that surface.
*/
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 6de4d3e..394660f 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -56,6 +56,8 @@
private static native int nativeGetWidth(long nativeObject);
private static native int nativeGetHeight(long nativeObject);
+ private static native long nativeGetNextFrameNumber(long nativeObject);
+
public static final Parcelable.Creator<Surface> CREATOR =
new Parcelable.Creator<Surface>() {
@Override
@@ -220,6 +222,18 @@
}
/**
+ * Returns the next frame number which will be dequeued for rendering.
+ * Intended for use with SurfaceFlinger's deferred transactions API.
+ *
+ * @hide
+ */
+ public long getNextFrameNumber() {
+ synchronized (mLock) {
+ return nativeGetNextFrameNumber(mNativeObject);
+ }
+ }
+
+ /**
* Returns true if the consumer of this Surface is running behind the producer.
*
* @return True if the consumer is more than one buffer ahead of the producer.
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index bcf9b2c..b58c68f 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -81,6 +81,9 @@
private static native boolean nativeSetActiveConfig(IBinder displayToken, int id);
private static native void nativeSetDisplayPowerMode(
IBinder displayToken, int mode);
+ private static native void nativeDeferTransactionUntil(long nativeObject,
+ IBinder handle, long frame);
+ private static native IBinder nativeGetHandle(long nativeObject);
private final CloseGuard mCloseGuard = CloseGuard.get();
@@ -358,6 +361,14 @@
nativeCloseTransaction();
}
+ public void deferTransactionUntil(IBinder handle, long frame) {
+ nativeDeferTransactionUntil(mNativeObject, handle, frame);
+ }
+
+ public IBinder getHandle() {
+ return nativeGetHandle(mNativeObject);
+ }
+
/** flag the transaction as an animation */
public static void setAnimationTransaction() {
nativeSetAnimationTransaction();
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index db68c29..dddea21 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -448,11 +448,10 @@
final boolean sizeChanged = mWidth != myWidth || mHeight != myHeight;
final boolean visibleChanged = mVisible != mRequestedVisible;
final boolean layoutSizeChanged = getWidth() != mLayout.width || getHeight() != mLayout.height;
+ final boolean positionChanged = mLeft != mLocation[0] || mTop != mLocation[1];
if (force || creating || formatChanged || sizeChanged || visibleChanged
- || mLeft != mLocation[0] || mTop != mLocation[1]
|| mUpdateWindowNeeded || mReportDrawNeeded || redrawNeeded || layoutSizeChanged) {
-
if (DEBUG) Log.i(TAG, "Changes: creating=" + creating
+ " format=" + formatChanged + " size=" + sizeChanged
+ " visible=" + visibleChanged
@@ -616,11 +615,22 @@
mSession.performDeferredDestroy(mWindow);
}
} catch (RemoteException ex) {
+ Log.e(TAG, "Exception from relayout", ex);
}
if (DEBUG) Log.v(
TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y +
" w=" + mLayout.width + " h=" + mLayout.height +
", frame=" + mSurfaceFrame);
+ } else if (positionChanged) { // Only the position has changed
+ mLeft = mLocation[0];
+ mTop = mLocation[1];
+ try {
+ mSession.repositionChild(mWindow, mLeft, mTop,
+ viewRoot != null ? viewRoot.getNextFrameNumber() : -1,
+ mWinFrame);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Exception from relayout", ex);
+ }
}
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 8b804e8..cd8c084 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -5455,7 +5455,7 @@
* @hide
*/
protected boolean performButtonActionOnTouchDown(MotionEvent event) {
- if (event.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE &&
+ if (event.isFromSource(InputDevice.SOURCE_MOUSE) &&
(event.getButtonState() & MotionEvent.BUTTON_SECONDARY) != 0) {
showContextMenu(event.getX(), event.getY());
mPrivateFlags |= PFLAG_CANCEL_NEXT_UP_EVENT;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index e17bdd7..faeb353 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -6607,6 +6607,20 @@
}
}
+ long getNextFrameNumber() {
+ long frameNumber = -1;
+ if (mSurfaceHolder != null) {
+ mSurfaceHolder.mSurfaceLock.lock();
+ }
+ if (mSurface.isValid()) {
+ frameNumber = mSurface.getNextFrameNumber();
+ }
+ if (mSurfaceHolder != null) {
+ mSurfaceHolder.mSurfaceLock.unlock();
+ }
+ return frameNumber;
+ }
+
class TakenSurfaceHolder extends BaseSurfaceHolder {
@Override
public boolean onAllowLockCanvas() {
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 5d11c8b..8259372 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -552,8 +552,8 @@
/**
* Called to move the window and its activity/task to a different stack container.
* For example, a window can move between
- * {@link android.app.ActivityManager#FULLSCREEN_WORKSPACE_STACK_ID} stack and
- * {@link android.app.ActivityManager#FREEFORM_WORKSPACE_STACK_ID} stack.
+ * {@link android.app.ActivityManager.StackId#FULLSCREEN_WORKSPACE_STACK_ID} stack and
+ * {@link android.app.ActivityManager.StackId#FREEFORM_WORKSPACE_STACK_ID} stack.
*
* @param stackId stack Id to change to.
*/
diff --git a/core/java/android/webkit/WebResourceRequest.java b/core/java/android/webkit/WebResourceRequest.java
index 23e9a0d..ab93505 100644
--- a/core/java/android/webkit/WebResourceRequest.java
+++ b/core/java/android/webkit/WebResourceRequest.java
@@ -40,9 +40,9 @@
boolean isForMainFrame();
/**
- * Gets whether the request was a result of a redirect.
+ * Gets whether the request was a result of a server-side redirect.
*
- * @return whether the request was a result of a redirect.
+ * @return whether the request was a result of a server-side redirect.
*/
boolean isRedirect();
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 584deff..cb18b49 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -96,27 +96,49 @@
public MissingWebViewPackageException(Exception e) { super(e); }
}
- public static String getWebViewPackageName() {
- return AppGlobals.getInitialApplication().getString(
- com.android.internal.R.string.config_webViewPackageName);
+ /** @hide */
+ public static String[] getWebViewPackageNames() {
+ return AppGlobals.getInitialApplication().getResources().getStringArray(
+ com.android.internal.R.array.config_webViewPackageNames);
}
- private static PackageInfo fetchPackageInfo() {
+ // TODO (gsennton) remove when committing webview xts test change
+ public static String getWebViewPackageName() {
+ String[] webViewPackageNames = getWebViewPackageNames();
+ return webViewPackageNames[webViewPackageNames.length-1];
+ }
+
+ /**
+ * Return the package info of the first package in the webview priority list that contains
+ * webview.
+ *
+ * @hide
+ */
+ public static PackageInfo findPreferredWebViewPackage() {
PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
- try {
- return pm.getPackageInfo(getWebViewPackageName(), PackageManager.GET_META_DATA);
- } catch (PackageManager.NameNotFoundException e) {
- throw new MissingWebViewPackageException(e);
+
+ for (String packageName : getWebViewPackageNames()) {
+ try {
+ PackageInfo packageInfo = pm.getPackageInfo(packageName,
+ PackageManager.GET_META_DATA);
+ ApplicationInfo applicationInfo = packageInfo.applicationInfo;
+
+ // If the correct flag is set the package contains webview.
+ if (getWebViewLibrary(applicationInfo) != null) {
+ return packageInfo;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ }
}
+ throw new MissingWebViewPackageException("Could not find a loadable WebView package");
}
// throws MissingWebViewPackageException
private static ApplicationInfo getWebViewApplicationInfo() {
- if (sPackageInfo == null) {
- return fetchPackageInfo().applicationInfo;
- } else {
+ if (sPackageInfo == null)
+ return findPreferredWebViewPackage().applicationInfo;
+ else
return sPackageInfo.applicationInfo;
- }
}
private static String getWebViewLibrary(ApplicationInfo ai) {
@@ -134,7 +156,12 @@
* name is the same as the one providing the webview.
*/
public static int loadWebViewNativeLibraryFromPackage(String packageName) {
- sPackageInfo = fetchPackageInfo();
+ try {
+ sPackageInfo = findPreferredWebViewPackage();
+ } catch (MissingWebViewPackageException e) {
+ return LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES;
+ }
+
if (packageName != null && packageName.equals(sPackageInfo.packageName)) {
return loadNativeLibrary();
}
@@ -180,7 +207,7 @@
private static Class<WebViewFactoryProvider> getProviderClass() {
try {
// First fetch the package info so we can log the webview package version.
- sPackageInfo = fetchPackageInfo();
+ sPackageInfo = findPreferredWebViewPackage();
Log.i(LOGTAG, "Loading " + sPackageInfo.packageName + " version " +
sPackageInfo.versionName + " (code " + sPackageInfo.versionCode + ")");
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index b3bd46d..1bce585 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -16,11 +16,8 @@
package com.android.internal.policy;
-import static android.app.ActivityManager.FIRST_DYNAMIC_STACK_ID;
-import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.INVALID_STACK_ID;
-import static android.app.ActivityManager.PINNED_STACK_ID;
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.view.View.MeasureSpec.AT_MOST;
import static android.view.View.MeasureSpec.EXACTLY;
import static android.view.View.MeasureSpec.getMode;
@@ -30,6 +27,7 @@
import android.animation.Animator;
import android.animation.ObjectAnimator;
+import android.app.ActivityManager.StackId;
import android.app.ActivityManagerNative;
import android.app.SearchManager;
import android.os.Build;
@@ -737,9 +735,9 @@
if (mWorkspaceId != workspaceId) {
mWorkspaceId = workspaceId;
// We might have to change the kind of surface before we do anything else.
- mNonClientDecorView.phoneWindowUpdated(hasNonClientDecor(mWorkspaceId),
- nonClientDecorHasShadow(mWorkspaceId));
- mDecor.enableNonClientDecor(hasNonClientDecor(workspaceId));
+ mNonClientDecorView.phoneWindowUpdated(StackId.hasWindowDecor(mWorkspaceId),
+ StackId.hasWindowShadow(mWorkspaceId));
+ mDecor.enableNonClientDecor(StackId.hasWindowDecor(workspaceId));
}
}
}
@@ -3735,7 +3733,7 @@
* @return Returns true when the window has a shadow created by the non client decor.
**/
private boolean windowHasShadow() {
- return windowHasNonClientDecor() && nonClientDecorHasShadow(mWindow.mWorkspaceId);
+ return windowHasNonClientDecor() && StackId.hasWindowShadow(mWindow.mWorkspaceId);
}
void setWindow(PhoneWindow phoneWindow) {
@@ -4234,7 +4232,7 @@
mWorkspaceId = getWorkspaceId();
// Only a non floating application window on one of the allowed workspaces can get a non
// client decor.
- if (!isFloating() && isApplication && mWorkspaceId < FIRST_DYNAMIC_STACK_ID) {
+ if (!isFloating() && isApplication && StackId.isStaticStack(mWorkspaceId)) {
// Dependent on the brightness of the used title we either use the
// dark or the light button frame.
if (nonClientDecorView == null) {
@@ -4250,12 +4248,13 @@
R.layout.non_client_decor_light, null);
}
}
- nonClientDecorView.setPhoneWindow(this, hasNonClientDecor(mWorkspaceId),
- nonClientDecorHasShadow(mWorkspaceId), getResizingBackgroundDrawable(),
+ nonClientDecorView.setPhoneWindow(this, StackId.hasWindowDecor(mWorkspaceId),
+ StackId.hasWindowShadow(mWorkspaceId), getResizingBackgroundDrawable(),
mDecor.getContext().getDrawable(R.drawable.non_client_decor_title_focused));
}
// Tell the decor if it has a visible non client decor.
- mDecor.enableNonClientDecor(nonClientDecorView != null && hasNonClientDecor(mWorkspaceId));
+ mDecor.enableNonClientDecor(
+ nonClientDecorView != null&& StackId.hasWindowDecor(mWorkspaceId));
return nonClientDecorView;
}
@@ -5428,24 +5427,6 @@
return workspaceId;
}
- /**
- * Determines if the window should show a non client decor for the workspace it is in.
- * @param workspaceId The Id of the workspace which contains this window.
- * @Return Returns true if the window should show a non client decor.
- **/
- private static boolean hasNonClientDecor(int workspaceId) {
- return workspaceId == FREEFORM_WORKSPACE_STACK_ID;
- }
-
- /**
- * Determines if the window should show a shadow or not, dependent on the workspace.
- * @param workspaceId The Id of the workspace which contains this window.
- * @Return Returns true if the window should show a shadow.
- **/
- private static boolean nonClientDecorHasShadow(int workspaceId) {
- return workspaceId == FREEFORM_WORKSPACE_STACK_ID || workspaceId == PINNED_STACK_ID;
- }
-
@Override
public void setTheme(int resid) {
mTheme = resid;
diff --git a/core/java/com/android/internal/widget/NonClientDecorView.java b/core/java/com/android/internal/widget/NonClientDecorView.java
index be9be11..de542b1 100644
--- a/core/java/com/android/internal/widget/NonClientDecorView.java
+++ b/core/java/com/android/internal/widget/NonClientDecorView.java
@@ -16,8 +16,9 @@
package com.android.internal.widget;
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+
import android.content.Context;
-import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Looper;
@@ -332,8 +333,7 @@
Window.WindowControllerCallback callback = mOwner.getWindowControllerCallback();
if (callback != null) {
try {
- callback.changeWindowStack(
- android.app.ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID);
+ callback.changeWindowStack(FULLSCREEN_WORKSPACE_STACK_ID);
} catch (RemoteException ex) {
Log.e(TAG, "Cannot change task workspace.");
}
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index 41aa9ca..0a8ae2b 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -722,33 +722,33 @@
// ----------------------------------------------------------------------------
static const JNINativeMethod gParcelMethods[] = {
- {"nativeDataSize", "(J)I", (void*)android_os_Parcel_dataSize},
- {"nativeDataAvail", "(J)I", (void*)android_os_Parcel_dataAvail},
- {"nativeDataPosition", "(J)I", (void*)android_os_Parcel_dataPosition},
- {"nativeDataCapacity", "(J)I", (void*)android_os_Parcel_dataCapacity},
- {"nativeSetDataSize", "(JI)J", (void*)android_os_Parcel_setDataSize},
- {"nativeSetDataPosition", "(JI)V", (void*)android_os_Parcel_setDataPosition},
- {"nativeSetDataCapacity", "(JI)V", (void*)android_os_Parcel_setDataCapacity},
+ {"nativeDataSize", "!(J)I", (void*)android_os_Parcel_dataSize},
+ {"nativeDataAvail", "!(J)I", (void*)android_os_Parcel_dataAvail},
+ {"nativeDataPosition", "!(J)I", (void*)android_os_Parcel_dataPosition},
+ {"nativeDataCapacity", "!(J)I", (void*)android_os_Parcel_dataCapacity},
+ {"nativeSetDataSize", "!(JI)J", (void*)android_os_Parcel_setDataSize},
+ {"nativeSetDataPosition", "!(JI)V", (void*)android_os_Parcel_setDataPosition},
+ {"nativeSetDataCapacity", "!(JI)V", (void*)android_os_Parcel_setDataCapacity},
- {"nativePushAllowFds", "(JZ)Z", (void*)android_os_Parcel_pushAllowFds},
- {"nativeRestoreAllowFds", "(JZ)V", (void*)android_os_Parcel_restoreAllowFds},
+ {"nativePushAllowFds", "!(JZ)Z", (void*)android_os_Parcel_pushAllowFds},
+ {"nativeRestoreAllowFds", "!(JZ)V", (void*)android_os_Parcel_restoreAllowFds},
{"nativeWriteByteArray", "(J[BII)V", (void*)android_os_Parcel_writeNative},
{"nativeWriteBlob", "(J[BII)V", (void*)android_os_Parcel_writeBlob},
- {"nativeWriteInt", "(JI)V", (void*)android_os_Parcel_writeInt},
- {"nativeWriteLong", "(JJ)V", (void*)android_os_Parcel_writeLong},
- {"nativeWriteFloat", "(JF)V", (void*)android_os_Parcel_writeFloat},
- {"nativeWriteDouble", "(JD)V", (void*)android_os_Parcel_writeDouble},
+ {"nativeWriteInt", "!(JI)V", (void*)android_os_Parcel_writeInt},
+ {"nativeWriteLong", "!(JJ)V", (void*)android_os_Parcel_writeLong},
+ {"nativeWriteFloat", "!(JF)V", (void*)android_os_Parcel_writeFloat},
+ {"nativeWriteDouble", "!(JD)V", (void*)android_os_Parcel_writeDouble},
{"nativeWriteString", "(JLjava/lang/String;)V", (void*)android_os_Parcel_writeString},
{"nativeWriteStrongBinder", "(JLandroid/os/IBinder;)V", (void*)android_os_Parcel_writeStrongBinder},
{"nativeWriteFileDescriptor", "(JLjava/io/FileDescriptor;)J", (void*)android_os_Parcel_writeFileDescriptor},
{"nativeCreateByteArray", "(J)[B", (void*)android_os_Parcel_createByteArray},
{"nativeReadBlob", "(J)[B", (void*)android_os_Parcel_readBlob},
- {"nativeReadInt", "(J)I", (void*)android_os_Parcel_readInt},
- {"nativeReadLong", "(J)J", (void*)android_os_Parcel_readLong},
- {"nativeReadFloat", "(J)F", (void*)android_os_Parcel_readFloat},
- {"nativeReadDouble", "(J)D", (void*)android_os_Parcel_readDouble},
+ {"nativeReadInt", "!(J)I", (void*)android_os_Parcel_readInt},
+ {"nativeReadLong", "!(J)J", (void*)android_os_Parcel_readLong},
+ {"nativeReadFloat", "!(J)F", (void*)android_os_Parcel_readFloat},
+ {"nativeReadDouble", "!(J)D", (void*)android_os_Parcel_readDouble},
{"nativeReadString", "(J)Ljava/lang/String;", (void*)android_os_Parcel_readString},
{"nativeReadStrongBinder", "(J)Landroid/os/IBinder;", (void*)android_os_Parcel_readStrongBinder},
{"nativeReadFileDescriptor", "(J)Ljava/io/FileDescriptor;", (void*)android_os_Parcel_readFileDescriptor},
@@ -765,7 +765,7 @@
{"nativeMarshall", "(J)[B", (void*)android_os_Parcel_marshall},
{"nativeUnmarshall", "(J[BII)J", (void*)android_os_Parcel_unmarshall},
{"nativeAppendFrom", "(JJII)J", (void*)android_os_Parcel_appendFrom},
- {"nativeHasFileDescriptors", "(J)Z", (void*)android_os_Parcel_hasFileDescriptors},
+ {"nativeHasFileDescriptors", "!(J)Z", (void*)android_os_Parcel_hasFileDescriptors},
{"nativeWriteInterfaceToken", "(JLjava/lang/String;)V", (void*)android_os_Parcel_writeInterfaceToken},
{"nativeEnforceInterface", "(JLjava/lang/String;)V", (void*)android_os_Parcel_enforceInterface},
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index c94bc64..55b7e7e 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -2156,9 +2156,9 @@
(void*) android_content_AssetManager_readAsset },
{ "seekAsset", "(JJI)J",
(void*) android_content_AssetManager_seekAsset },
- { "getAssetLength", "(J)J",
+ { "getAssetLength", "!(J)J",
(void*) android_content_AssetManager_getAssetLength },
- { "getAssetRemainingLength", "(J)J",
+ { "getAssetRemainingLength", "!(J)J",
(void*) android_content_AssetManager_getAssetRemainingLength },
{ "addAssetPathNative", "(Ljava/lang/String;Z)I",
(void*) android_content_AssetManager_addAssetPath },
@@ -2174,25 +2174,25 @@
(void*) android_content_AssetManager_getLocales },
{ "getSizeConfigurations", "()[Landroid/content/res/Configuration;",
(void*) android_content_AssetManager_getSizeConfigurations },
- { "setConfiguration", "(IILjava/lang/String;IIIIIIIIIIIIII)V",
+ { "setConfiguration", "!(IILjava/lang/String;IIIIIIIIIIIIII)V",
(void*) android_content_AssetManager_setConfiguration },
- { "getResourceIdentifier","(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
+ { "getResourceIdentifier","!(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
(void*) android_content_AssetManager_getResourceIdentifier },
- { "getResourceName","(I)Ljava/lang/String;",
+ { "getResourceName","!(I)Ljava/lang/String;",
(void*) android_content_AssetManager_getResourceName },
- { "getResourcePackageName","(I)Ljava/lang/String;",
+ { "getResourcePackageName","!(I)Ljava/lang/String;",
(void*) android_content_AssetManager_getResourcePackageName },
- { "getResourceTypeName","(I)Ljava/lang/String;",
+ { "getResourceTypeName","!(I)Ljava/lang/String;",
(void*) android_content_AssetManager_getResourceTypeName },
- { "getResourceEntryName","(I)Ljava/lang/String;",
+ { "getResourceEntryName","!(I)Ljava/lang/String;",
(void*) android_content_AssetManager_getResourceEntryName },
- { "loadResourceValue","(ISLandroid/util/TypedValue;Z)I",
+ { "loadResourceValue","!(ISLandroid/util/TypedValue;Z)I",
(void*) android_content_AssetManager_loadResourceValue },
- { "loadResourceBagValue","(IILandroid/util/TypedValue;Z)I",
+ { "loadResourceBagValue","!(IILandroid/util/TypedValue;Z)I",
(void*) android_content_AssetManager_loadResourceBagValue },
- { "getStringBlockCount","()I",
+ { "getStringBlockCount","!()I",
(void*) android_content_AssetManager_getStringBlockCount },
- { "getNativeStringBlock","(I)J",
+ { "getNativeStringBlock","!(I)J",
(void*) android_content_AssetManager_getNativeStringBlock },
{ "getCookieName","(I)Ljava/lang/String;",
(void*) android_content_AssetManager_getCookieName },
@@ -2210,21 +2210,21 @@
(void*) android_content_AssetManager_copyTheme },
{ "clearTheme", "(J)V",
(void*) android_content_AssetManager_clearTheme },
- { "loadThemeAttributeValue", "(JILandroid/util/TypedValue;Z)I",
+ { "loadThemeAttributeValue", "!(JILandroid/util/TypedValue;Z)I",
(void*) android_content_AssetManager_loadThemeAttributeValue },
- { "getThemeChangingConfigurations", "(J)I",
+ { "getThemeChangingConfigurations", "!(J)I",
(void*) android_content_AssetManager_getThemeChangingConfigurations },
{ "dumpTheme", "(JILjava/lang/String;Ljava/lang/String;)V",
(void*) android_content_AssetManager_dumpTheme },
- { "applyStyle","(JIIJ[I[I[I)Z",
+ { "applyStyle","!(JIIJ[I[I[I)Z",
(void*) android_content_AssetManager_applyStyle },
- { "resolveAttrs","(JII[I[I[I[I)Z",
+ { "resolveAttrs","!(JII[I[I[I[I)Z",
(void*) android_content_AssetManager_resolveAttrs },
- { "retrieveAttributes","(J[I[I[I)Z",
+ { "retrieveAttributes","!(J[I[I[I)Z",
(void*) android_content_AssetManager_retrieveAttributes },
- { "getArraySize","(I)I",
+ { "getArraySize","!(I)I",
(void*) android_content_AssetManager_getArraySize },
- { "retrieveArray","(I[I)I",
+ { "retrieveArray","!(I[I)I",
(void*) android_content_AssetManager_retrieveArray },
// XML files.
@@ -2234,11 +2234,11 @@
// Arrays.
{ "getArrayStringResource","(I)[Ljava/lang/String;",
(void*) android_content_AssetManager_getArrayStringResource },
- { "getArrayStringInfo","(I)[I",
+ { "getArrayStringInfo","!(I)[I",
(void*) android_content_AssetManager_getArrayStringInfo },
- { "getArrayIntResource","(I)[I",
+ { "getArrayIntResource","!(I)[I",
(void*) android_content_AssetManager_getArrayIntResource },
- { "getStyleAttributes","(I)[I",
+ { "getStyleAttributes","!(I)[I",
(void*) android_content_AssetManager_getStyleAttributes },
// Bookkeeping.
diff --git a/core/jni/android_util_XmlBlock.cpp b/core/jni/android_util_XmlBlock.cpp
index 7ae51c8..a15c23c 100644
--- a/core/jni/android_util_XmlBlock.cpp
+++ b/core/jni/android_util_XmlBlock.cpp
@@ -372,37 +372,37 @@
(void*) android_content_XmlBlock_nativeGetStringBlock },
{ "nativeCreateParseState", "(J)J",
(void*) android_content_XmlBlock_nativeCreateParseState },
- { "nativeNext", "(J)I",
+ { "nativeNext", "!(J)I",
(void*) android_content_XmlBlock_nativeNext },
- { "nativeGetNamespace", "(J)I",
+ { "nativeGetNamespace", "!(J)I",
(void*) android_content_XmlBlock_nativeGetNamespace },
- { "nativeGetName", "(J)I",
+ { "nativeGetName", "!(J)I",
(void*) android_content_XmlBlock_nativeGetName },
- { "nativeGetText", "(J)I",
+ { "nativeGetText", "!(J)I",
(void*) android_content_XmlBlock_nativeGetText },
- { "nativeGetLineNumber", "(J)I",
+ { "nativeGetLineNumber", "!(J)I",
(void*) android_content_XmlBlock_nativeGetLineNumber },
- { "nativeGetAttributeCount", "(J)I",
+ { "nativeGetAttributeCount", "!(J)I",
(void*) android_content_XmlBlock_nativeGetAttributeCount },
- { "nativeGetAttributeNamespace","(JI)I",
+ { "nativeGetAttributeNamespace","!(JI)I",
(void*) android_content_XmlBlock_nativeGetAttributeNamespace },
- { "nativeGetAttributeName", "(JI)I",
+ { "nativeGetAttributeName", "!(JI)I",
(void*) android_content_XmlBlock_nativeGetAttributeName },
- { "nativeGetAttributeResource", "(JI)I",
+ { "nativeGetAttributeResource", "!(JI)I",
(void*) android_content_XmlBlock_nativeGetAttributeResource },
- { "nativeGetAttributeDataType", "(JI)I",
+ { "nativeGetAttributeDataType", "!(JI)I",
(void*) android_content_XmlBlock_nativeGetAttributeDataType },
- { "nativeGetAttributeData", "(JI)I",
+ { "nativeGetAttributeData", "!(JI)I",
(void*) android_content_XmlBlock_nativeGetAttributeData },
- { "nativeGetAttributeStringValue", "(JI)I",
+ { "nativeGetAttributeStringValue", "!(JI)I",
(void*) android_content_XmlBlock_nativeGetAttributeStringValue },
- { "nativeGetAttributeIndex", "(JLjava/lang/String;Ljava/lang/String;)I",
+ { "nativeGetAttributeIndex", "!(JLjava/lang/String;Ljava/lang/String;)I",
(void*) android_content_XmlBlock_nativeGetAttributeIndex },
- { "nativeGetIdAttribute", "(J)I",
+ { "nativeGetIdAttribute", "!(J)I",
(void*) android_content_XmlBlock_nativeGetIdAttribute },
- { "nativeGetClassAttribute", "(J)I",
+ { "nativeGetClassAttribute", "!(J)I",
(void*) android_content_XmlBlock_nativeGetClassAttribute },
- { "nativeGetStyleAttribute", "(J)I",
+ { "nativeGetStyleAttribute", "!(J)I",
(void*) android_content_XmlBlock_nativeGetStyleAttribute },
{ "nativeDestroyParseState", "(J)V",
(void*) android_content_XmlBlock_nativeDestroyParseState },
diff --git a/core/jni/android_view_DisplayListCanvas.cpp b/core/jni/android_view_DisplayListCanvas.cpp
index b64acc3..47df786 100644
--- a/core/jni/android_view_DisplayListCanvas.cpp
+++ b/core/jni/android_view_DisplayListCanvas.cpp
@@ -23,6 +23,7 @@
#include <android_runtime/AndroidRuntime.h>
#include <cutils/properties.h>
+#include <EGL/egl.h>
#include <SkBitmap.h>
#include <SkRegion.h>
@@ -146,15 +147,17 @@
// ----------------------------------------------------------------------------
static jboolean android_view_DisplayListCanvas_isAvailable(JNIEnv* env, jobject clazz) {
- char prop[PROPERTY_VALUE_MAX];
- if (property_get("ro.kernel.qemu", prop, NULL) == 0) {
- // not in the emulator
- return JNI_TRUE;
+ static EGLint numES2Configs = -1;
+
+ if (numES2Configs == -1) {
+ EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ EGLint major; EGLint minor;
+ eglInitialize(display, &major, &minor);
+ EGLint configAttribs[] = {EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_NONE};
+ eglChooseConfig(display, configAttribs, NULL, 0, &numES2Configs);
}
- // In the emulator this property will be set to 1 when hardware GLES is
- // enabled, 0 otherwise. On old emulator versions it will be undefined.
- property_get("ro.kernel.qemu.gles", prop, "0");
- return atoi(prop) == 1 ? JNI_TRUE : JNI_FALSE;
+
+ return (numES2Configs > 0) ? JNI_TRUE : JNI_FALSE;
}
// ----------------------------------------------------------------------------
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index da96b93..ff51e4e 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -460,6 +460,10 @@
anw->query(anw, NATIVE_WINDOW_HEIGHT, &value);
return value;
}
+static jlong nativeGetNextFrameNumber(JNIEnv *env, jclass clazz, jlong nativeObject) {
+ Surface* surface = reinterpret_cast<Surface*>(nativeObject);
+ return surface->getNextFrameNumber();
+}
namespace uirenderer {
@@ -536,6 +540,7 @@
(void*)nativeWriteToParcel },
{"nativeGetWidth", "(J)I", (void*)nativeGetWidth },
{"nativeGetHeight", "(J)I", (void*)nativeGetHeight },
+ {"nativeGetNextFrameNumber", "(J)J", (void*)nativeGetNextFrameNumber },
// HWUI context
{"nHwuiCreate", "(JJ)J", (void*) hwui::create },
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 931ad54..1dfe40a 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -571,6 +571,21 @@
return JNI_TRUE;
}
+
+static void nativeDeferTransactionUntil(JNIEnv* env, jclass clazz, jlong nativeObject,
+ jobject handleObject, jlong frameNumber) {
+ auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
+ sp<IBinder> handle = ibinderForJavaObject(env, handleObject);
+
+ ctrl->deferTransactionUntil(handle, frameNumber);
+}
+
+static jobject nativeGetHandle(JNIEnv* env, jclass clazz, jlong nativeObject) {
+ auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
+
+ return javaObjectForIBinder(env, ctrl->getHandle());
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod sSurfaceControlMethods[] = {
@@ -638,6 +653,10 @@
(void*)nativeGetAnimationFrameStats },
{"nativeSetDisplayPowerMode", "(Landroid/os/IBinder;I)V",
(void*)nativeSetDisplayPowerMode },
+ {"nativeDeferTransactionUntil", "(JLandroid/os/IBinder;J)V",
+ (void*)nativeDeferTransactionUntil },
+ {"nativeGetHandle", "(J)Landroid/os/IBinder;",
+ (void*)nativeGetHandle }
};
int register_android_view_SurfaceControl(JNIEnv* env)
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 5828829..561bcbc 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2127,7 +2127,7 @@
<!-- Allows an application to grant specific permissions.
@hide -->
<permission android:name="android.permission.GRANT_RUNTIME_PERMISSIONS"
- android:protectionLevel="signature|installer" />
+ android:protectionLevel="signature|installer|verifier" />
<!-- Allows an app that has this permission and the permissions to install packages
to request certain runtime permissions to be granted at installation.
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 400c822..76f7062 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2130,8 +2130,10 @@
string that's stored in 8-bit unpacked format) characters.-->
<bool translatable="false" name="config_sms_decode_gsm_8bit_data">false</bool>
- <!-- Package name providing WebView implementation. -->
- <string name="config_webViewPackageName" translatable="false">com.android.webview</string>
+ <!-- List of package names (ordered by preference) providing WebView implementations. -->
+ <string-array name="config_webViewPackageNames" translatable="false">
+ <item>com.android.webview</item>
+ </string-array>
<!-- If EMS is not supported, framework breaks down EMS into single segment SMS
and adds page info " x/y". This config is used to set which carrier doesn't
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d1932fc..ebef6cc 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2019,7 +2019,7 @@
<java-symbol type="attr" name="actionModeWebSearchDrawable" />
<java-symbol type="string" name="websearch" />
<java-symbol type="drawable" name="ic_media_video_poster" />
- <java-symbol type="string" name="config_webViewPackageName" />
+ <java-symbol type="array" name="config_webViewPackageNames" />
<!-- From SubtitleView -->
<java-symbol type="dimen" name="subtitle_corner_radius" />
diff --git a/core/tests/benchmarks/src/android/content/res/ResourcesBenchmark.java b/core/tests/benchmarks/src/android/content/res/ResourcesBenchmark.java
new file mode 100644
index 0000000..3638473
--- /dev/null
+++ b/core/tests/benchmarks/src/android/content/res/ResourcesBenchmark.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2015 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.content.res;
+
+import android.util.AttributeSet;
+import android.util.Xml;
+
+import com.android.internal.R;
+import com.google.caliper.SimpleBenchmark;
+
+import org.xmlpull.v1.XmlPullParser;
+
+public class ResourcesBenchmark extends SimpleBenchmark {
+
+ private AssetManager mAsset;
+ private Resources mRes;
+
+ private int mTextId;
+ private int mColorId;
+ private int mIntegerId;
+ private int mLayoutId;
+
+ @Override
+ protected void setUp() {
+ mAsset = new AssetManager();
+ mAsset.addAssetPath("/system/framework/framework-res.apk");
+ mRes = new Resources(mAsset, null, null);
+
+ mTextId = mRes.getIdentifier("cancel", "string", "android");
+ mColorId = mRes.getIdentifier("transparent", "color", "android");
+ mIntegerId = mRes.getIdentifier("config_shortAnimTime", "integer", "android");
+ mLayoutId = mRes.getIdentifier("two_line_list_item", "layout", "android");
+ }
+
+ @Override
+ protected void tearDown() {
+ mAsset.close();
+ }
+
+ public void timeGetString(int reps) {
+ for (int i = 0; i < reps; i++) {
+ mRes.getText(mTextId);
+ }
+ }
+
+ public void timeGetColor(int reps) {
+ for (int i = 0; i < reps; i++) {
+ mRes.getColor(mColorId, null);
+ }
+ }
+
+ public void timeGetInteger(int reps) {
+ for (int i = 0; i < reps; i++) {
+ mRes.getInteger(mIntegerId);
+ }
+ }
+
+ public void timeGetLayoutAndTraverse(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ final XmlResourceParser parser = mRes.getLayout(mLayoutId);
+ try {
+ while (parser.next() != XmlPullParser.END_DOCUMENT) {
+ // Walk the entire tree
+ }
+ } finally {
+ parser.close();
+ }
+ }
+ }
+}
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 3c10368..6903b7b 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -136,7 +136,9 @@
</intent-filter>
</activity>
- <activity android:name="android.widget.TextViewActivity" android:label="TextViewActivity">
+ <activity android:name="android.widget.TextViewActivity"
+ android:label="TextViewActivity"
+ android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index 6a76a27..bb51570 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -36,7 +36,6 @@
import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.SmallTest;
-import android.util.OrientationUtil;
import android.view.KeyEvent;
/**
@@ -44,16 +43,13 @@
*/
public class TextViewActivityTest extends ActivityInstrumentationTestCase2<TextViewActivity>{
- private OrientationUtil mOrientationUtil;
-
public TextViewActivityTest() {
super(TextViewActivity.class);
}
@Override
public void setUp() {
- mOrientationUtil = OrientationUtil.initializeAndStartActivityIfNotStarted(this);
- mOrientationUtil.setPortraitOrientation();
+ getActivity();
}
@SmallTest
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index 4f56965..4d2037b 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -812,8 +812,7 @@
setTileModeY(parseTileMode(tileModeY));
}
- final int densityDpi = r.getDisplayMetrics().densityDpi;
- state.mTargetDensity = densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
+ state.mTargetDensity = Drawable.resolveDensity(r, 0);
}
@Override
@@ -975,13 +974,7 @@
* after inflating or applying a theme.
*/
private void updateLocalState(Resources res) {
- if (res != null) {
- final int densityDpi = res.getDisplayMetrics().densityDpi;
- mTargetDensity = densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
- } else {
- mTargetDensity = mBitmapState.mTargetDensity;
- }
-
+ mTargetDensity = resolveDensity(res, mBitmapState.mTargetDensity);
mTintFilter = updateTintFilter(mTintFilter, mBitmapState.mTint, mBitmapState.mTintMode);
computeBitmapSize();
}
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index ff28777..39d13df 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -1079,8 +1079,7 @@
// to the compatibility density only to have them scaled back up when
// drawn to the screen.
if (opts == null) opts = new BitmapFactory.Options();
- opts.inScreenDensity = res != null
- ? res.getDisplayMetrics().noncompatDensityDpi : DisplayMetrics.DENSITY_DEVICE;
+ opts.inScreenDensity = Drawable.resolveDensity(res, 0);
Bitmap bm = BitmapFactory.decodeResourceStream(res, value, is, pad, opts);
if (bm != null) {
byte[] np = bm.getNinePatchChunk();
@@ -1338,6 +1337,65 @@
}
/**
+ * Scales a floating-point pixel value from the source density to the
+ * target density.
+ *
+ * @param pixels the pixel value for use in source density
+ * @param sourceDensity the source density
+ * @param targetDensity the target density
+ * @return the scaled pixel value for use in target density
+ */
+ static float scaleFromDensity(float pixels, int sourceDensity, int targetDensity) {
+ return pixels * targetDensity / sourceDensity;
+ }
+
+ /**
+ * Scales a pixel value from the source density to the target density,
+ * optionally handling the resulting pixel value as a size rather than an
+ * offset.
+ * <p>
+ * A size conversion involves rounding the base value and ensuring that
+ * a non-zero base value is at least one pixel in size.
+ * <p>
+ * An offset conversion involves simply truncating the base value to an
+ * integer.
+ *
+ * @param pixels the pixel value for use in source density
+ * @param sourceDensity the source density
+ * @param targetDensity the target density
+ * @param isSize {@code true} to handle the resulting scaled value as a
+ * size, or {@code false} to handle it as an offset
+ * @return the scaled pixel value for use in target density
+ */
+ static int scaleFromDensity(
+ int pixels, int sourceDensity, int targetDensity, boolean isSize) {
+ if (pixels == 0 || sourceDensity == targetDensity) {
+ return pixels;
+ }
+
+ final float result = pixels * targetDensity / (float) sourceDensity;
+ if (!isSize) {
+ return (int) result;
+ }
+
+ final int rounded = Math.round(result);
+ if (rounded != 0) {
+ return rounded;
+ } else if (pixels == 0) {
+ return 0;
+ } else if (pixels > 0) {
+ return 1;
+ } else {
+ return -1;
+ }
+ }
+
+ static int resolveDensity(@NonNull Resources r, int parentDensity) {
+ final int densityDpi = r == null ? parentDensity : r.getDisplayMetrics().densityDpi;
+ return densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
+ }
+
+ /**
* Parses a {@link android.graphics.PorterDuff.Mode} from a tintMode
* attribute's enum value.
*
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index ac72734..b2044e1 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -701,13 +701,8 @@
DrawableContainerState(DrawableContainerState orig, DrawableContainer owner,
Resources res) {
mOwner = owner;
-
- final Resources sourceRes = res != null ? res : (orig != null ? orig.mSourceRes : null);
- mSourceRes = sourceRes;
-
- final int densityDpi = sourceRes == null ? 0 : sourceRes.getDisplayMetrics().densityDpi;
- final int sourceDensity = densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
- mDensity = sourceDensity;
+ mSourceRes = res != null ? res : (orig != null ? orig.mSourceRes : null);
+ mDensity = Drawable.resolveDensity(res, orig != null ? orig.mDensity : 0);
if (orig != null) {
mChangingConfigurations = orig.mChangingConfigurations;
@@ -731,7 +726,7 @@
mHasTintList = orig.mHasTintList;
mHasTintMode = orig.mHasTintMode;
- if (orig.mDensity == sourceDensity) {
+ if (orig.mDensity == mDensity) {
if (orig.mCheckedPadding) {
mConstantPadding = new Rect(orig.mConstantPadding);
mCheckedPadding = true;
@@ -903,13 +898,11 @@
// The density may have changed since the last update (if any). Any
// dimension-type attributes will need their default values scaled.
- final int densityDpi = res.getDisplayMetrics().densityDpi;
- final int newSourceDensity = densityDpi == 0 ?
- DisplayMetrics.DENSITY_DEFAULT : densityDpi;
- final int oldSourceDensity = mDensity;
- mDensity = newSourceDensity;
+ final int targetDensity = Drawable.resolveDensity(res, mDensity);
+ final int sourceDensity = mDensity;
+ mDensity = targetDensity;
- if (oldSourceDensity != newSourceDensity) {
+ if (sourceDensity != targetDensity) {
mCheckedConstantSize = false;
mCheckedPadding = false;
}
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index d7fd8a5..4be86ef 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -17,11 +17,13 @@
package android.graphics.drawable;
import android.annotation.ColorInt;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
+import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
@@ -40,6 +42,7 @@
import android.graphics.Shader;
import android.graphics.SweepGradient;
import android.util.AttributeSet;
+import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
@@ -1136,10 +1139,14 @@
}
@Override
- public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
+ public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
+ @NonNull AttributeSet attrs, @Nullable Theme theme)
throws XmlPullParserException, IOException {
+ super.inflate(r, parser, attrs, theme);
+
+ mGradientState.setDensity(Drawable.resolveDensity(r, 0));
+
final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.GradientDrawable);
- super.inflateWithAttributes(r, parser, a, R.styleable.GradientDrawable_visible);
updateStateFromTypedArray(a);
a.recycle();
@@ -1149,7 +1156,7 @@
}
@Override
- public void applyTheme(Theme t) {
+ public void applyTheme(@NonNull Theme t) {
super.applyTheme(t);
final GradientState state = mGradientState;
@@ -1157,6 +1164,8 @@
return;
}
+ state.setDensity(Drawable.resolveDensity(t.getResources(), 0));
+
if (state.mThemeAttrs != null) {
final TypedArray a = t.resolveAttributes(
state.mThemeAttrs, R.styleable.GradientDrawable);
@@ -1668,7 +1677,7 @@
@Override
public Drawable mutate() {
if (!mMutated && super.mutate() == this) {
- mGradientState = new GradientState(mGradientState);
+ mGradientState = new GradientState(mGradientState, null);
updateLocalState(null);
mMutated = true;
}
@@ -1723,6 +1732,8 @@
ColorStateList mTint = null;
PorterDuff.Mode mTintMode = DEFAULT_TINT_MODE;
+ int mDensity = DisplayMetrics.DENSITY_DEFAULT;
+
int[] mThemeAttrs;
int[] mAttrSize;
int[] mAttrGradient;
@@ -1736,55 +1747,145 @@
setGradientColors(gradientColors);
}
- public GradientState(GradientState state) {
- mChangingConfigurations = state.mChangingConfigurations;
- mShape = state.mShape;
- mGradient = state.mGradient;
- mAngle = state.mAngle;
- mOrientation = state.mOrientation;
- mSolidColors = state.mSolidColors;
- if (state.mGradientColors != null) {
- mGradientColors = state.mGradientColors.clone();
+ public GradientState(@NonNull GradientState orig, @Nullable Resources res) {
+ mChangingConfigurations = orig.mChangingConfigurations;
+ mShape = orig.mShape;
+ mGradient = orig.mGradient;
+ mAngle = orig.mAngle;
+ mOrientation = orig.mOrientation;
+ mSolidColors = orig.mSolidColors;
+ if (orig.mGradientColors != null) {
+ mGradientColors = orig.mGradientColors.clone();
}
- if (state.mPositions != null) {
- mPositions = state.mPositions.clone();
+ if (orig.mPositions != null) {
+ mPositions = orig.mPositions.clone();
}
- mStrokeColors = state.mStrokeColors;
- mStrokeWidth = state.mStrokeWidth;
- mStrokeDashWidth = state.mStrokeDashWidth;
- mStrokeDashGap = state.mStrokeDashGap;
- mRadius = state.mRadius;
- if (state.mRadiusArray != null) {
- mRadiusArray = state.mRadiusArray.clone();
+ mStrokeColors = orig.mStrokeColors;
+ mStrokeWidth = orig.mStrokeWidth;
+ mStrokeDashWidth = orig.mStrokeDashWidth;
+ mStrokeDashGap = orig.mStrokeDashGap;
+ mRadius = orig.mRadius;
+ if (orig.mRadiusArray != null) {
+ mRadiusArray = orig.mRadiusArray.clone();
}
- if (state.mPadding != null) {
- mPadding = new Rect(state.mPadding);
+ if (orig.mPadding != null) {
+ mPadding = new Rect(orig.mPadding);
}
- mWidth = state.mWidth;
- mHeight = state.mHeight;
- mInnerRadiusRatio = state.mInnerRadiusRatio;
- mThicknessRatio = state.mThicknessRatio;
- mInnerRadius = state.mInnerRadius;
- mThickness = state.mThickness;
- mDither = state.mDither;
- mOpticalInsets = state.mOpticalInsets;
- mCenterX = state.mCenterX;
- mCenterY = state.mCenterY;
- mGradientRadius = state.mGradientRadius;
- mGradientRadiusType = state.mGradientRadiusType;
- mUseLevel = state.mUseLevel;
- mUseLevelForShape = state.mUseLevelForShape;
- mOpaqueOverBounds = state.mOpaqueOverBounds;
- mOpaqueOverShape = state.mOpaqueOverShape;
- mTint = state.mTint;
- mTintMode = state.mTintMode;
- mThemeAttrs = state.mThemeAttrs;
- mAttrSize = state.mAttrSize;
- mAttrGradient = state.mAttrGradient;
- mAttrSolid = state.mAttrSolid;
- mAttrStroke = state.mAttrStroke;
- mAttrCorners = state.mAttrCorners;
- mAttrPadding = state.mAttrPadding;
+ mWidth = orig.mWidth;
+ mHeight = orig.mHeight;
+ mInnerRadiusRatio = orig.mInnerRadiusRatio;
+ mThicknessRatio = orig.mThicknessRatio;
+ mInnerRadius = orig.mInnerRadius;
+ mThickness = orig.mThickness;
+ mDither = orig.mDither;
+ mOpticalInsets = orig.mOpticalInsets;
+ mCenterX = orig.mCenterX;
+ mCenterY = orig.mCenterY;
+ mGradientRadius = orig.mGradientRadius;
+ mGradientRadiusType = orig.mGradientRadiusType;
+ mUseLevel = orig.mUseLevel;
+ mUseLevelForShape = orig.mUseLevelForShape;
+ mOpaqueOverBounds = orig.mOpaqueOverBounds;
+ mOpaqueOverShape = orig.mOpaqueOverShape;
+ mTint = orig.mTint;
+ mTintMode = orig.mTintMode;
+ mThemeAttrs = orig.mThemeAttrs;
+ mAttrSize = orig.mAttrSize;
+ mAttrGradient = orig.mAttrGradient;
+ mAttrSolid = orig.mAttrSolid;
+ mAttrStroke = orig.mAttrStroke;
+ mAttrCorners = orig.mAttrCorners;
+ mAttrPadding = orig.mAttrPadding;
+
+ mDensity = Drawable.resolveDensity(res, orig.mDensity);
+ if (orig.mDensity != mDensity) {
+ applyDensityScaling(orig.mDensity, mDensity);
+ }
+ }
+
+ /**
+ * Sets the constant state density.
+ * <p>
+ * If the density has been previously set, dispatches the change to
+ * subclasses so that density-dependent properties may be scaled as
+ * necessary.
+ *
+ * @param targetDensity the new constant state density
+ */
+ public final void setDensity(int targetDensity) {
+ if (mDensity != targetDensity) {
+ final int sourceDensity = mDensity;
+ mDensity = targetDensity;
+
+ applyDensityScaling(sourceDensity, targetDensity);
+ }
+ }
+
+ private void applyDensityScaling(int sourceDensity, int targetDensity) {
+ if (mInnerRadius > 0) {
+ mInnerRadius = Drawable.scaleFromDensity(
+ mInnerRadius, sourceDensity, targetDensity, true);
+ }
+ if (mThickness > 0) {
+ mThickness = Drawable.scaleFromDensity(
+ mThickness, sourceDensity, targetDensity, true);
+ }
+ if (mOpticalInsets != Insets.NONE) {
+ final int left = Drawable.scaleFromDensity(
+ mOpticalInsets.left, sourceDensity, targetDensity, true);
+ final int top = Drawable.scaleFromDensity(
+ mOpticalInsets.top, sourceDensity, targetDensity, true);
+ final int right = Drawable.scaleFromDensity(
+ mOpticalInsets.right, sourceDensity, targetDensity, true);
+ final int bottom = Drawable.scaleFromDensity(
+ mOpticalInsets.bottom, sourceDensity, targetDensity, true);
+ mOpticalInsets = Insets.of(left, top, right, bottom);
+ }
+ if (mPadding != null) {
+ mPadding.left = Drawable.scaleFromDensity(
+ mPadding.left, sourceDensity, targetDensity, false);
+ mPadding.top = Drawable.scaleFromDensity(
+ mPadding.top, sourceDensity, targetDensity, false);
+ mPadding.right = Drawable.scaleFromDensity(
+ mPadding.right, sourceDensity, targetDensity, false);
+ mPadding.bottom = Drawable.scaleFromDensity(
+ mPadding.bottom, sourceDensity, targetDensity, false);
+ }
+ if (mRadius > 0) {
+ mRadius = Drawable.scaleFromDensity(mRadius, sourceDensity, targetDensity);
+ }
+ if (mRadiusArray != null) {
+ mRadiusArray[0] = Drawable.scaleFromDensity(
+ (int) mRadiusArray[0], sourceDensity, targetDensity, true);
+ mRadiusArray[1] = Drawable.scaleFromDensity(
+ (int) mRadiusArray[1], sourceDensity, targetDensity, true);
+ mRadiusArray[2] = Drawable.scaleFromDensity(
+ (int) mRadiusArray[2], sourceDensity, targetDensity, true);
+ mRadiusArray[3] = Drawable.scaleFromDensity(
+ (int) mRadiusArray[3], sourceDensity, targetDensity, true);
+ }
+ if (mStrokeWidth > 0) {
+ mStrokeWidth = Drawable.scaleFromDensity(
+ mStrokeWidth, sourceDensity, targetDensity, true);
+ }
+ if (mStrokeDashWidth > 0) {
+ mStrokeDashWidth = Drawable.scaleFromDensity(
+ mStrokeDashGap, sourceDensity, targetDensity);
+ }
+ if (mStrokeDashGap > 0) {
+ mStrokeDashGap = Drawable.scaleFromDensity(
+ mStrokeDashGap, sourceDensity, targetDensity);
+ }
+ if (mGradientRadiusType == RADIUS_TYPE_PIXELS) {
+ mGradientRadius = Drawable.scaleFromDensity(
+ mGradientRadius, sourceDensity, targetDensity);
+ }
+ if (mWidth > 0) {
+ mWidth = Drawable.scaleFromDensity(mWidth, sourceDensity, targetDensity, true);
+ }
+ if (mHeight > 0) {
+ mHeight = Drawable.scaleFromDensity(mHeight, sourceDensity, targetDensity, true);
+ }
}
@Override
@@ -1805,8 +1906,18 @@
}
@Override
- public Drawable newDrawable(Resources res) {
- return new GradientDrawable(this, res);
+ public Drawable newDrawable(@Nullable Resources res) {
+ // If this drawable is being created for a different density,
+ // just create a new constant state and call it a day.
+ final GradientState state;
+ final int density = Drawable.resolveDensity(res, mDensity);
+ if (density != mDensity) {
+ state = new GradientState(this, res);
+ } else {
+ state = this;
+ }
+
+ return new GradientDrawable(state, res);
}
@Override
@@ -1913,7 +2024,7 @@
*
* @param state Constant state from which the drawable inherits
*/
- private GradientDrawable(GradientState state, Resources res) {
+ private GradientDrawable(@NonNull GradientState state, @Nullable Resources res) {
mGradientState = state;
updateLocalState(res);
diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index 4368fe9..3405030 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -170,8 +170,7 @@
// The density may have changed since the last update. This will
// apply scaling to any existing constant state properties.
- final int densityDpi = r.getDisplayMetrics().densityDpi;
- final int density = densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
+ final int density = Drawable.resolveDensity(r, 0);
state.setDensity(density);
final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.LayerDrawable);
@@ -200,8 +199,7 @@
return;
}
- final int densityDpi = t.getResources().getDisplayMetrics().densityDpi;
- final int density = densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
+ final int density = Drawable.resolveDensity(t.getResources(), 0);
state.setDensity(density);
if (state.mThemeAttrs != null) {
@@ -1800,9 +1798,7 @@
mGravity = orig.mGravity;
mId = orig.mId;
- final int densityDpi = res == null ? orig.mDensity : res.getDisplayMetrics().densityDpi;
- mDensity = densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
-
+ mDensity = Drawable.resolveDensity(res, orig.mDensity);
if (orig.mDensity != mDensity) {
applyDensityScaling(orig.mDensity, mDensity);
}
@@ -1823,21 +1819,21 @@
}
private void applyDensityScaling(int sourceDensity, int targetDensity) {
- mInsetL = Bitmap.scaleFromDensity(mInsetL, sourceDensity, targetDensity);
- mInsetT = Bitmap.scaleFromDensity(mInsetT, sourceDensity, targetDensity);
- mInsetR = Bitmap.scaleFromDensity(mInsetR, sourceDensity, targetDensity);
- mInsetB = Bitmap.scaleFromDensity(mInsetB, sourceDensity, targetDensity);
+ mInsetL = Drawable.scaleFromDensity(mInsetL, sourceDensity, targetDensity, false);
+ mInsetT = Drawable.scaleFromDensity(mInsetT, sourceDensity, targetDensity, false);
+ mInsetR = Drawable.scaleFromDensity(mInsetR, sourceDensity, targetDensity, false);
+ mInsetB = Drawable.scaleFromDensity(mInsetB, sourceDensity, targetDensity, false);
if (mInsetS != UNDEFINED_INSET) {
- mInsetS = Bitmap.scaleFromDensity(mInsetS, sourceDensity, targetDensity);
+ mInsetS = Drawable.scaleFromDensity(mInsetS, sourceDensity, targetDensity, false);
}
if (mInsetE != UNDEFINED_INSET) {
- mInsetE = Bitmap.scaleFromDensity(mInsetE, sourceDensity, targetDensity);
+ mInsetE = Drawable.scaleFromDensity(mInsetE, sourceDensity, targetDensity, false);
}
if (mWidth > 0) {
- mWidth = Bitmap.scaleFromDensity(mWidth, sourceDensity, targetDensity);
+ mWidth = Drawable.scaleFromDensity(mWidth, sourceDensity, targetDensity, true);
}
if (mHeight > 0) {
- mHeight = Bitmap.scaleFromDensity(mHeight, sourceDensity, targetDensity);
+ mHeight = Drawable.scaleFromDensity(mHeight, sourceDensity, targetDensity, true);
}
}
}
@@ -1874,16 +1870,7 @@
LayerState(@Nullable LayerState orig, @NonNull LayerDrawable owner,
@Nullable Resources res) {
- final int densityDpi;
- if (res != null) {
- densityDpi = res.getDisplayMetrics().densityDpi;
- } else if (orig != null) {
- densityDpi = orig.mDensity;
- } else {
- densityDpi = 0;
- }
-
- mDensity = densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
+ mDensity = Drawable.resolveDensity(res, orig != null ? orig.mDensity : 0);
if (orig != null) {
final ChildDrawable[] origChildDrawable = orig.mChildren;
@@ -1939,28 +1926,28 @@
private void applyDensityScaling(int sourceDensity, int targetDensity) {
if (mPaddingLeft > 0) {
- mPaddingLeft = Bitmap.scaleFromDensity(
- mPaddingLeft, sourceDensity, targetDensity);
+ mPaddingLeft = Drawable.scaleFromDensity(
+ mPaddingLeft, sourceDensity, targetDensity, false);
}
if (mPaddingTop > 0) {
- mPaddingTop = Bitmap.scaleFromDensity(
- mPaddingTop, sourceDensity, targetDensity);
+ mPaddingTop = Drawable.scaleFromDensity(
+ mPaddingTop, sourceDensity, targetDensity, false);
}
if (mPaddingRight > 0) {
- mPaddingRight = Bitmap.scaleFromDensity(
- mPaddingRight, sourceDensity, targetDensity);
+ mPaddingRight = Drawable.scaleFromDensity(
+ mPaddingRight, sourceDensity, targetDensity, false);
}
if (mPaddingBottom > 0) {
- mPaddingBottom = Bitmap.scaleFromDensity(
- mPaddingBottom, sourceDensity, targetDensity);
+ mPaddingBottom = Drawable.scaleFromDensity(
+ mPaddingBottom, sourceDensity, targetDensity, false);
}
if (mPaddingStart > 0) {
- mPaddingStart = Bitmap.scaleFromDensity(
- mPaddingStart, sourceDensity, targetDensity);
+ mPaddingStart = Drawable.scaleFromDensity(
+ mPaddingStart, sourceDensity, targetDensity, false);
}
if (mPaddingEnd > 0) {
- mPaddingEnd = Bitmap.scaleFromDensity(
- mPaddingEnd, sourceDensity, targetDensity);
+ mPaddingEnd = Drawable.scaleFromDensity(
+ mPaddingEnd, sourceDensity, targetDensity, false);
}
}
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index 231405d..4d51d63 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -181,10 +181,10 @@
}
private static Insets scaleFromDensity(Insets insets, int sdensity, int tdensity) {
- int left = Bitmap.scaleFromDensity(insets.left, sdensity, tdensity);
- int top = Bitmap.scaleFromDensity(insets.top, sdensity, tdensity);
- int right = Bitmap.scaleFromDensity(insets.right, sdensity, tdensity);
- int bottom = Bitmap.scaleFromDensity(insets.bottom, sdensity, tdensity);
+ int left = Drawable.scaleFromDensity(insets.left, sdensity, tdensity, true);
+ int top = Drawable.scaleFromDensity(insets.top, sdensity, tdensity, true);
+ int right = Drawable.scaleFromDensity(insets.right, sdensity, tdensity, true);
+ int bottom = Drawable.scaleFromDensity(insets.bottom, sdensity, tdensity, true);
return Insets.of(left, top, right, bottom);
}
@@ -196,18 +196,20 @@
mBitmapHeight = mNinePatch.getHeight();
mOpticalInsets = mNinePatchState.mOpticalInsets;
} else {
- mBitmapWidth = Bitmap.scaleFromDensity(mNinePatch.getWidth(), sdensity, tdensity);
- mBitmapHeight = Bitmap.scaleFromDensity(mNinePatch.getHeight(), sdensity, tdensity);
+ mBitmapWidth = Drawable.scaleFromDensity(
+ mNinePatch.getWidth(), sdensity, tdensity, true);
+ mBitmapHeight = Drawable.scaleFromDensity(
+ mNinePatch.getHeight(), sdensity, tdensity, true);
if (mNinePatchState.mPadding != null && mPadding != null) {
Rect dest = mPadding;
Rect src = mNinePatchState.mPadding;
if (dest == src) {
mPadding = dest = new Rect(src);
}
- dest.left = Bitmap.scaleFromDensity(src.left, sdensity, tdensity);
- dest.top = Bitmap.scaleFromDensity(src.top, sdensity, tdensity);
- dest.right = Bitmap.scaleFromDensity(src.right, sdensity, tdensity);
- dest.bottom = Bitmap.scaleFromDensity(src.bottom, sdensity, tdensity);
+ dest.left = Drawable.scaleFromDensity(src.left, sdensity, tdensity, true);
+ dest.top = Drawable.scaleFromDensity(src.top, sdensity, tdensity, true);
+ dest.right = Drawable.scaleFromDensity(src.right, sdensity, tdensity, true);
+ dest.bottom = Drawable.scaleFromDensity(src.bottom, sdensity, tdensity, true);
}
mOpticalInsets = scaleFromDensity(mNinePatchState.mOpticalInsets, sdensity, tdensity);
}
@@ -490,8 +492,7 @@
state.mTint = tint;
}
- final int densityDpi = r.getDisplayMetrics().densityDpi;
- state.mTargetDensity = densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
+ state.mTargetDensity = Drawable.resolveDensity(r, state.mTargetDensity);
}
@Override
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index a196fad..52e7f24 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -1008,7 +1008,8 @@
private void applyDensityScaling(int sourceDensity, int targetDensity) {
if (mMaxRadius != RADIUS_AUTO) {
- mMaxRadius = Bitmap.scaleFromDensity(mMaxRadius, sourceDensity, targetDensity);
+ mMaxRadius = Drawable.scaleFromDensity(
+ mMaxRadius, sourceDensity, targetDensity, true);
}
}
@@ -1039,16 +1040,13 @@
private RippleDrawable(RippleState state, Resources res) {
mState = new RippleState(state, this, res);
mLayerState = mState;
+ mDensity = Drawable.resolveDensity(res, mState.mDensity);
if (mState.mNum > 0) {
ensurePadding();
refreshPadding();
}
- if (res != null) {
- mDensity = res.getDisplayMetrics().density;
- }
-
updateLocalState();
}
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index e161f3d..eee9b24 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -259,13 +259,7 @@
* displayed, or {@code null} to use the constant state defaults
*/
private void updateLocalState(Resources res) {
- if (res != null) {
- final int densityDpi = res.getDisplayMetrics().densityDpi;
- mTargetDensity = densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
- } else {
- mTargetDensity = mVectorState.mVPathRenderer.mSourceDensity;
- }
-
+ mTargetDensity = Drawable.resolveDensity(res, mVectorState.mVPathRenderer.mSourceDensity);
mTintFilter = updateTintFilter(mTintFilter, mVectorState.mTint, mVectorState.mTintMode);
computeVectorSize();
}
@@ -453,18 +447,18 @@
final int sourceDensity = pathRenderer.mSourceDensity;
final int targetDensity = mTargetDensity;
if (targetDensity != sourceDensity) {
- mDpiScaledWidth = Bitmap.scaleFromDensity(
- (int) pathRenderer.mBaseWidth, sourceDensity, targetDensity);
- mDpiScaledHeight = Bitmap.scaleFromDensity(
- (int) pathRenderer.mBaseHeight,sourceDensity, targetDensity);
- final int left = Bitmap.scaleFromDensity(
- opticalInsets.left, sourceDensity, targetDensity);
- final int right = Bitmap.scaleFromDensity(
- opticalInsets.right, sourceDensity, targetDensity);
- final int top = Bitmap.scaleFromDensity(
- opticalInsets.top, sourceDensity, targetDensity);
- final int bottom = Bitmap.scaleFromDensity(
- opticalInsets.bottom, sourceDensity, targetDensity);
+ mDpiScaledWidth = Drawable.scaleFromDensity(
+ (int) pathRenderer.mBaseWidth, sourceDensity, targetDensity, true);
+ mDpiScaledHeight = Drawable.scaleFromDensity(
+ (int) pathRenderer.mBaseHeight,sourceDensity, targetDensity, true);
+ final int left = Drawable.scaleFromDensity(
+ opticalInsets.left, sourceDensity, targetDensity, false);
+ final int right = Drawable.scaleFromDensity(
+ opticalInsets.right, sourceDensity, targetDensity, false);
+ final int top = Drawable.scaleFromDensity(
+ opticalInsets.top, sourceDensity, targetDensity, false);
+ final int bottom = Drawable.scaleFromDensity(
+ opticalInsets.bottom, sourceDensity, targetDensity, false);
mDpiScaledInsets = Insets.of(left, top, right, bottom);
} else {
mDpiScaledWidth = (int) pathRenderer.mBaseWidth;
@@ -600,11 +594,10 @@
// The density may have changed since the last update (if any). Any
// dimension-type attributes will need their default values scaled.
- final int densityDpi = a.getResources().getDisplayMetrics().densityDpi;
- final int newSourceDensity = densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
- final int oldSourceDensity = pathRenderer.mSourceDensity;
- final float densityScale = newSourceDensity / (float) oldSourceDensity;
- pathRenderer.mSourceDensity = newSourceDensity;
+ final int targetDensity = Drawable.resolveDensity(a.getResources(), 0);
+ final int sourceDensity = pathRenderer.mSourceDensity;
+ final float densityScale = targetDensity / (float) sourceDensity;
+ pathRenderer.mSourceDensity = targetDensity;
final int tintMode = a.getInt(R.styleable.VectorDrawable_tintMode, -1);
if (tintMode != -1) {
@@ -647,16 +640,16 @@
"<vector> tag requires height > 0");
}
- final int insetLeft = a.getDimensionPixelSize(
+ final int insetLeft = a.getDimensionPixelOffset(
R.styleable.VectorDrawable_opticalInsetLeft,
(int) (pathRenderer.mOpticalInsets.left * densityScale));
- final int insetTop = a.getDimensionPixelSize(
+ final int insetTop = a.getDimensionPixelOffset(
R.styleable.VectorDrawable_opticalInsetTop,
(int) (pathRenderer.mOpticalInsets.top * densityScale));
- final int insetRight = a.getDimensionPixelSize(
+ final int insetRight = a.getDimensionPixelOffset(
R.styleable.VectorDrawable_opticalInsetRight,
(int) (pathRenderer.mOpticalInsets.right * densityScale));
- final int insetBottom = a.getDimensionPixelSize(
+ final int insetBottom = a.getDimensionPixelOffset(
R.styleable.VectorDrawable_opticalInsetBottom,
(int) (pathRenderer.mOpticalInsets.bottom * densityScale));
pathRenderer.mOpticalInsets = Insets.of(insetLeft, insetTop, insetRight, insetBottom);
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index d94c91d..ae5fa6c 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -56,6 +56,7 @@
Layer.cpp \
LayerCache.cpp \
LayerRenderer.cpp \
+ LayerUpdateQueue.cpp \
Matrix.cpp \
OpenGLRenderer.cpp \
Patch.cpp \
@@ -204,6 +205,7 @@
unit_tests/ClipAreaTests.cpp \
unit_tests/DamageAccumulatorTests.cpp \
unit_tests/FatVectorTests.cpp \
+ unit_tests/LayerUpdateQueueTests.cpp \
unit_tests/LinearAllocatorTests.cpp \
unit_tests/StringUtilsTests.cpp
diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp
index 0868853..2fca5ea 100644
--- a/libs/hwui/BakedOpRenderer.cpp
+++ b/libs/hwui/BakedOpRenderer.cpp
@@ -20,6 +20,7 @@
#include "Glop.h"
#include "GlopBuilder.h"
#include "renderstate/RenderState.h"
+#include "utils/FatVector.h"
#include "utils/GLUtils.h"
namespace android {
@@ -29,10 +30,13 @@
// OffscreenBuffer
////////////////////////////////////////////////////////////////////////////////
-OffscreenBuffer::OffscreenBuffer(Caches& caches, uint32_t textureWidth, uint32_t textureHeight,
+OffscreenBuffer::OffscreenBuffer(RenderState& renderState, Caches& caches,
+ uint32_t textureWidth, uint32_t textureHeight,
uint32_t viewportWidth, uint32_t viewportHeight)
- : texture(caches)
- , texCoords(0, viewportHeight / float(textureHeight), viewportWidth / float(textureWidth), 0) {
+ : renderState(renderState)
+ , viewportWidth(viewportWidth)
+ , viewportHeight(viewportHeight)
+ , texture(caches) {
texture.width = textureWidth;
texture.height = textureHeight;
@@ -48,16 +52,72 @@
GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
}
+void OffscreenBuffer::updateMeshFromRegion() {
+ // avoid T-junctions as they cause artifacts in between the resultant
+ // geometry when complex transforms occur.
+ // TODO: generate the safeRegion only if necessary based on drawing transform
+ Region safeRegion = Region::createTJunctionFreeRegion(region);
+
+ size_t count;
+ const android::Rect* rects = safeRegion.getArray(&count);
+
+ const float texX = 1.0f / float(viewportWidth);
+ const float texY = 1.0f / float(viewportHeight);
+
+ FatVector<TextureVertex, 64> meshVector(count * 4); // uses heap if more than 64 vertices needed
+ TextureVertex* mesh = &meshVector[0];
+ for (size_t i = 0; i < count; i++) {
+ const android::Rect* r = &rects[i];
+
+ const float u1 = r->left * texX;
+ const float v1 = (viewportHeight - r->top) * texY;
+ const float u2 = r->right * texX;
+ const float v2 = (viewportHeight - r->bottom) * texY;
+
+ TextureVertex::set(mesh++, r->left, r->top, u1, v1);
+ TextureVertex::set(mesh++, r->right, r->top, u2, v1);
+ TextureVertex::set(mesh++, r->left, r->bottom, u1, v2);
+ TextureVertex::set(mesh++, r->right, r->bottom, u2, v2);
+ }
+ elementCount = count * 6;
+ renderState.meshState().genOrUpdateMeshBuffer(&vbo,
+ sizeof(TextureVertex) * count * 4,
+ &meshVector[0],
+ GL_DYNAMIC_DRAW); // TODO: GL_STATIC_DRAW if savelayer
+}
+
+OffscreenBuffer::~OffscreenBuffer() {
+ texture.deleteTexture();
+ renderState.meshState().deleteMeshBuffer(vbo);
+ elementCount = 0;
+ vbo = 0;
+}
+
////////////////////////////////////////////////////////////////////////////////
// BakedOpRenderer
////////////////////////////////////////////////////////////////////////////////
-OffscreenBuffer* BakedOpRenderer::startLayer(uint32_t width, uint32_t height) {
+OffscreenBuffer* BakedOpRenderer::createOffscreenBuffer(RenderState& renderState,
+ uint32_t width, uint32_t height) {
+ // TODO: get from cache!
+ return new OffscreenBuffer(renderState, Caches::getInstance(), width, height, width, height);
+}
+
+void BakedOpRenderer::destroyOffscreenBuffer(OffscreenBuffer* offscreenBuffer) {
+ // TODO: return texture/offscreenbuffer to cache!
+ delete offscreenBuffer;
+}
+
+OffscreenBuffer* BakedOpRenderer::createLayer(uint32_t width, uint32_t height) {
LOG_ALWAYS_FATAL_IF(mRenderTarget.offscreenBuffer, "already has layer...");
- // TODO: really should be caching these!
- OffscreenBuffer* buffer = new OffscreenBuffer(mCaches, width, height, width, height);
- mRenderTarget.offscreenBuffer = buffer;
+ OffscreenBuffer* buffer = createOffscreenBuffer(mRenderState, width, height);
+ startLayer(buffer);
+ return buffer;
+}
+
+void BakedOpRenderer::startLayer(OffscreenBuffer* offscreenBuffer) {
+ mRenderTarget.offscreenBuffer = offscreenBuffer;
// create and bind framebuffer
mRenderTarget.frameBufferId = mRenderState.genFramebuffer();
@@ -65,7 +125,7 @@
// attach the texture to the FBO
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
- buffer->texture.id, 0);
+ offscreenBuffer->texture.id, 0);
LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(), "startLayer FAILED");
LOG_ALWAYS_FATAL_IF(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE,
"framebuffer incomplete!");
@@ -75,11 +135,11 @@
glClear(GL_COLOR_BUFFER_BIT);
// Change the viewport & ortho projection
- setViewport(width, height);
- return buffer;
+ setViewport(offscreenBuffer->viewportWidth, offscreenBuffer->viewportHeight);
}
void BakedOpRenderer::endLayer() {
+ mRenderTarget.offscreenBuffer->updateMeshFromRegion();
mRenderTarget.offscreenBuffer = nullptr;
// Detach the texture from the FBO
@@ -144,6 +204,12 @@
mRenderState.scissor().set(clip.left, mRenderTarget.viewportHeight - clip.bottom,
clip.getWidth(), clip.getHeight());
}
+ if (mRenderTarget.offscreenBuffer) { // TODO: not with multi-draw
+ // register layer damage to draw-back region
+ const Rect& uiDirty = state.computedState.clippedBounds;
+ android::Rect dirty(uiDirty.left, uiDirty.top, uiDirty.right, uiDirty.bottom);
+ mRenderTarget.offscreenBuffer->region.orSelf(dirty);
+ }
mRenderState.render(glop, mRenderTarget.orthoMatrix);
mHasDrawn = true;
}
@@ -156,6 +222,14 @@
LOG_ALWAYS_FATAL("unsupported operation");
}
+void BakedOpDispatcher::onBeginLayerOp(BakedOpRenderer& renderer, const BeginLayerOp& op, const BakedOpState& state) {
+ LOG_ALWAYS_FATAL("unsupported operation");
+}
+
+void BakedOpDispatcher::onEndLayerOp(BakedOpRenderer& renderer, const EndLayerOp& op, const BakedOpState& state) {
+ LOG_ALWAYS_FATAL("unsupported operation");
+}
+
void BakedOpDispatcher::onBitmapOp(BakedOpRenderer& renderer, const BitmapOp& op, const BakedOpState& state) {
renderer.caches().textureState().activateTexture(0); // TODO: should this be automatic, and/or elsewhere?
Texture* texture = renderer.getTexture(op.bitmap);
@@ -199,36 +273,26 @@
renderer.renderGlop(state, glop);
}
-void BakedOpDispatcher::onBeginLayerOp(BakedOpRenderer& renderer, const BeginLayerOp& op, const BakedOpState& state) {
- LOG_ALWAYS_FATAL("unsupported operation");
-}
-
-void BakedOpDispatcher::onEndLayerOp(BakedOpRenderer& renderer, const EndLayerOp& op, const BakedOpState& state) {
- LOG_ALWAYS_FATAL("unsupported operation");
-}
-
void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op, const BakedOpState& state) {
OffscreenBuffer* buffer = *op.layerHandle;
// TODO: extend this to handle HW layers & paint properties which
// reside in node.properties().layerProperties()
- float layerAlpha = (op.paint->getAlpha() / 255.0f) * state.alpha;
- const bool tryToSnap = state.computedState.transform.isPureTranslate();
+ float layerAlpha = op.alpha * state.alpha;
Glop glop;
GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
.setRoundRectClipState(state.roundRectClipState)
- .setMeshTexturedUvQuad(nullptr, buffer->texCoords)
- .setFillLayer(buffer->texture, op.paint->getColorFilter(), layerAlpha, PaintUtils::getXfermodeDirect(op.paint), Blend::ModeOrderSwap::NoSwap)
+ .setMeshTexturedIndexedVbo(buffer->vbo, buffer->elementCount)
+ .setFillLayer(buffer->texture, op.colorFilter, layerAlpha, op.mode, Blend::ModeOrderSwap::NoSwap)
.setTransform(state.computedState.transform, TransformFlags::None)
- .setModelViewMapUnitToRectOptionalSnap(tryToSnap, op.unmappedBounds)
+ .setModelViewOffsetRectSnap(op.unmappedBounds.left, op.unmappedBounds.top,
+ Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight()))
.build();
renderer.renderGlop(state, glop);
- // destroy and delete, since each clipped saveLayer is only drawn once.
- buffer->texture.deleteTexture();
-
- // TODO: return texture/offscreenbuffer to cache!
- delete buffer;
+ if (op.destroy) {
+ BakedOpRenderer::destroyOffscreenBuffer(buffer);
+ }
}
} // namespace uirenderer
diff --git a/libs/hwui/BakedOpRenderer.h b/libs/hwui/BakedOpRenderer.h
index 16afad4..aa1e67d 100644
--- a/libs/hwui/BakedOpRenderer.h
+++ b/libs/hwui/BakedOpRenderer.h
@@ -35,12 +35,24 @@
*/
class OffscreenBuffer {
public:
- OffscreenBuffer(Caches& caches, uint32_t textureWidth, uint32_t textureHeight,
+ OffscreenBuffer(RenderState& renderState, Caches& caches,
+ uint32_t textureWidth, uint32_t textureHeight,
uint32_t viewportWidth, uint32_t viewportHeight);
+ ~OffscreenBuffer();
+ // must be called prior to rendering, to construct/update vertex buffer
+ void updateMeshFromRegion();
+
+ RenderState& renderState;
+ uint32_t viewportWidth;
+ uint32_t viewportHeight;
Texture texture;
- Rect texCoords;
+
+ // Portion of offscreen buffer that has been drawn to. Used to minimize drawing area when
+ // drawing back to screen / parent FBO.
Region region;
+ GLsizei elementCount = 0;
+ GLuint vbo = 0;
};
/**
@@ -60,12 +72,17 @@
, mOpaque(opaque) {
}
+ static OffscreenBuffer* createOffscreenBuffer(RenderState& renderState,
+ uint32_t width, uint32_t height);
+ static void destroyOffscreenBuffer(OffscreenBuffer*);
+
RenderState& renderState() { return mRenderState; }
Caches& caches() { return mCaches; }
void startFrame(uint32_t width, uint32_t height);
void endFrame();
- OffscreenBuffer* startLayer(uint32_t width, uint32_t height);
+ OffscreenBuffer* createLayer(uint32_t width, uint32_t height);
+ void startLayer(OffscreenBuffer* offscreenBuffer);
void endLayer();
Texture* getTexture(const SkBitmap* bitmap);
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index 86796c5..00c4e2d 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -154,7 +154,11 @@
return allocator.usedSize();
}
bool isEmpty() {
+#if HWUI_NEW_OPS
+ return ops.empty();
+#else
return !hasDrawOps;
+#endif
}
private:
@@ -179,7 +183,7 @@
// List of functors
LsaVector<Functor*> functors;
- bool hasDrawOps;
+ bool hasDrawOps; // only used if !HWUI_NEW_OPS
void cleanupResources();
};
diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp
index d2da851..f3ac93b 100644
--- a/libs/hwui/GlopBuilder.cpp
+++ b/libs/hwui/GlopBuilder.cpp
@@ -70,6 +70,20 @@
// Mesh
////////////////////////////////////////////////////////////////////////////////
+GlopBuilder& GlopBuilder::setMeshTexturedIndexedVbo(GLuint vbo, GLsizei elementCount) {
+ TRIGGER_STAGE(kMeshStage);
+
+ mOutGlop->mesh.primitiveMode = GL_TRIANGLES;
+ mOutGlop->mesh.indices = { mRenderState.meshState().getQuadListIBO(), nullptr };
+ mOutGlop->mesh.vertices = {
+ vbo,
+ VertexAttribFlags::TextureCoord,
+ nullptr, nullptr, nullptr,
+ kTextureVertexStride };
+ mOutGlop->mesh.elementCount = elementCount;
+ return *this;
+}
+
GlopBuilder& GlopBuilder::setMeshUnitQuad() {
TRIGGER_STAGE(kMeshStage);
diff --git a/libs/hwui/GlopBuilder.h b/libs/hwui/GlopBuilder.h
index 6f5802e..6270dcb 100644
--- a/libs/hwui/GlopBuilder.h
+++ b/libs/hwui/GlopBuilder.h
@@ -47,6 +47,7 @@
public:
GlopBuilder(RenderState& renderState, Caches& caches, Glop* outGlop);
+ GlopBuilder& setMeshTexturedIndexedVbo(GLuint vbo, GLsizei elementCount);
GlopBuilder& setMeshUnitQuad();
GlopBuilder& setMeshTexturedUnitQuad(const UvMapper* uvMapper);
GlopBuilder& setMeshTexturedUvQuad(const UvMapper* uvMapper, const Rect uvs);
diff --git a/libs/hwui/LayerUpdateQueue.cpp b/libs/hwui/LayerUpdateQueue.cpp
new file mode 100644
index 0000000..db5f676
--- /dev/null
+++ b/libs/hwui/LayerUpdateQueue.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2015 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 "LayerUpdateQueue.h"
+
+#include "RenderNode.h"
+
+namespace android {
+namespace uirenderer {
+
+void LayerUpdateQueue::clear() {
+ mEntries.clear();
+}
+
+void LayerUpdateQueue::enqueueLayerWithDamage(RenderNode* renderNode, Rect damage) {
+ damage.doIntersect(0, 0, renderNode->getWidth(), renderNode->getHeight());
+ if (!damage.isEmpty()) {
+ for (Entry& entry : mEntries) {
+ if (CC_UNLIKELY(entry.renderNode == renderNode)) {
+ entry.damage.unionWith(damage);
+ return;
+ }
+ }
+ mEntries.emplace_back(renderNode, damage);
+ }
+}
+
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/LayerUpdateQueue.h b/libs/hwui/LayerUpdateQueue.h
new file mode 100644
index 0000000..be612d2
--- /dev/null
+++ b/libs/hwui/LayerUpdateQueue.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 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_LAYER_UPDATE_QUEUE_H
+#define ANDROID_HWUI_LAYER_UPDATE_QUEUE_H
+
+#include "Rect.h"
+#include "utils/Macros.h"
+
+#include <vector>
+#include <unordered_map>
+
+namespace android {
+namespace uirenderer {
+
+class RenderNode;
+
+class LayerUpdateQueue {
+ PREVENT_COPY_AND_ASSIGN(LayerUpdateQueue);
+public:
+ struct Entry {
+ Entry(RenderNode* renderNode, const Rect& damage)
+ : renderNode(renderNode)
+ , damage(damage) {}
+ RenderNode* renderNode;
+ Rect damage;
+ };
+
+ LayerUpdateQueue() {}
+ void enqueueLayerWithDamage(RenderNode* renderNode, Rect dirty);
+ void clear();
+ const std::vector<Entry> entries() const { return mEntries; }
+private:
+ std::vector<Entry> mEntries;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_LAYER_UPDATE_QUEUE_H
diff --git a/libs/hwui/OpReorderer.cpp b/libs/hwui/OpReorderer.cpp
index ddeb336..163f7cc 100644
--- a/libs/hwui/OpReorderer.cpp
+++ b/libs/hwui/OpReorderer.cpp
@@ -18,6 +18,7 @@
#include "utils/PaintUtils.h"
#include "RenderNode.h"
+#include "LayerUpdateQueue.h"
#include "SkCanvas.h"
#include "utils/Trace.h"
@@ -202,6 +203,14 @@
Rect mClipRect;
};
+OpReorderer::LayerReorderer::LayerReorderer(uint32_t width, uint32_t height,
+ const BeginLayerOp* beginLayerOp, RenderNode* renderNode)
+ : width(width)
+ , height(height)
+ , offscreenBuffer(renderNode ? renderNode->getLayer() : nullptr)
+ , beginLayerOp(beginLayerOp)
+ , renderNode(renderNode) {}
+
// iterate back toward target to see if anything drawn since should overlap the new op
// if no target, merging ops still iterate to find similar batch to insert after
void OpReorderer::LayerReorderer::locateInsertIndex(int batchId, const Rect& clippedBounds,
@@ -288,33 +297,48 @@
}
void OpReorderer::LayerReorderer::dump() const {
+ ALOGD("LayerReorderer %p, %ux%u buffer %p, blo %p, rn %p",
+ this, width, height, offscreenBuffer, beginLayerOp, renderNode);
for (const BatchBase* batch : mBatches) {
batch->dump();
}
}
-OpReorderer::OpReorderer(const SkRect& clip, uint32_t viewportWidth, uint32_t viewportHeight,
+OpReorderer::OpReorderer(const LayerUpdateQueue& layers, const SkRect& clip,
+ uint32_t viewportWidth, uint32_t viewportHeight,
const std::vector< sp<RenderNode> >& nodes)
: mCanvasState(*this) {
ATRACE_NAME("prepare drawing commands");
-
mLayerReorderers.emplace_back(viewportWidth, viewportHeight);
- mLayerStack.push_back(0);
+ mLayerStack.push_back(0);
mCanvasState.initializeSaveStack(viewportWidth, viewportHeight,
clip.fLeft, clip.fTop, clip.fRight, clip.fBottom,
Vector3());
+
+ // Render all layers to be updated, in order. Defer in reverse order, so that they'll be
+ // updated in the order they're passed in (mLayerReorderers are issued to Renderer in reverse)
+ for (int i = layers.entries().size() - 1; i >= 0; i--) {
+ RenderNode* layerNode = layers.entries()[i].renderNode;
+ const Rect& layerDamage = layers.entries()[i].damage;
+
+ saveForLayer(layerNode->getWidth(), layerNode->getHeight(), nullptr, layerNode);
+ mCanvasState.writableSnapshot()->setClip(
+ layerDamage.left, layerDamage.top, layerDamage.right, layerDamage.bottom);
+
+ if (layerNode->getDisplayList()) {
+ deferImpl(*(layerNode->getDisplayList()));
+ }
+ restoreForLayer();
+ }
+
+ // Defer Fbo0
for (const sp<RenderNode>& node : nodes) {
if (node->nothingToDraw()) continue;
- // TODO: dedupe this code with onRenderNode()
- mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
- if (node->applyViewProperties(mCanvasState)) {
- // not rejected do ops...
- const DisplayList& displayList = node->getDisplayList();
- deferImpl(displayList);
- }
- mCanvasState.restore();
+ int count = mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
+ deferNodePropsAndOps(*node);
+ mCanvasState.restoreToCount(count);
}
}
@@ -334,6 +358,23 @@
void OpReorderer::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}
+void OpReorderer::deferNodePropsAndOps(RenderNode& node) {
+ if (node.applyViewProperties(mCanvasState)) {
+ // not rejected so render
+ if (node.getLayer()) {
+ // HW layer
+ LayerOp* drawLayerOp = new (mAllocator) LayerOp(node);
+ BakedOpState* bakedOpState = tryBakeOpState(*drawLayerOp);
+ if (bakedOpState) {
+ // Layer will be drawn into parent layer (which is now current, since we popped mLayerStack)
+ currentLayer().deferUnmergeableOp(mAllocator, bakedOpState, OpBatchType::Bitmap);
+ }
+ } else {
+ deferImpl(*(node.getDisplayList()));
+ }
+ }
+}
+
/**
* Used to define a list of lambdas referencing private OpReorderer::onXXXXOp() methods.
*
@@ -365,11 +406,9 @@
mCanvasState.clipRect(op.localClipRect.left, op.localClipRect.top,
op.localClipRect.right, op.localClipRect.bottom, SkRegion::kIntersect_Op);
- // apply RenderProperties state
- if (op.renderNode->applyViewProperties(mCanvasState)) {
- // if node not rejected based on properties, do ops...
- deferImpl(op.renderNode->getDisplayList());
- }
+ // then apply state from node properties, and defer ops
+ deferNodePropsAndOps(*op.renderNode);
+
mCanvasState.restoreToCount(count);
}
@@ -400,10 +439,8 @@
currentLayer().deferUnmergeableOp(mAllocator, bakedStateOp, OpBatchType::Vertices);
}
-// TODO: test rejection at defer time, where the bounds become empty
-void OpReorderer::onBeginLayerOp(const BeginLayerOp& op) {
- const uint32_t layerWidth = (uint32_t) op.unmappedBounds.getWidth();
- const uint32_t layerHeight = (uint32_t) op.unmappedBounds.getHeight();
+void OpReorderer::saveForLayer(uint32_t layerWidth, uint32_t layerHeight,
+ const BeginLayerOp* beginLayerOp, RenderNode* renderNode) {
mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
mCanvasState.writableSnapshot()->transform->loadIdentity();
@@ -412,18 +449,27 @@
// create a new layer, and push its index on the stack
mLayerStack.push_back(mLayerReorderers.size());
- mLayerReorderers.emplace_back(layerWidth, layerHeight);
- mLayerReorderers.back().beginLayerOp = &op;
+ mLayerReorderers.emplace_back(layerWidth, layerHeight, beginLayerOp, renderNode);
+}
+
+void OpReorderer::restoreForLayer() {
+ // restore canvas, and pop finished layer off of the stack
+ mCanvasState.restore();
+ mLayerStack.pop_back();
+}
+
+// TODO: test rejection at defer time, where the bounds become empty
+void OpReorderer::onBeginLayerOp(const BeginLayerOp& op) {
+ const uint32_t layerWidth = (uint32_t) op.unmappedBounds.getWidth();
+ const uint32_t layerHeight = (uint32_t) op.unmappedBounds.getHeight();
+ saveForLayer(layerWidth, layerHeight, &op, nullptr);
}
void OpReorderer::onEndLayerOp(const EndLayerOp& /* ignored */) {
- mCanvasState.restore();
-
const BeginLayerOp& beginLayerOp = *currentLayer().beginLayerOp;
-
- // pop finished layer off of the stack
int finishedLayerIndex = mLayerStack.back();
- mLayerStack.pop_back();
+
+ restoreForLayer();
// record the draw operation into the previous layer's list of draw commands
// uses state from the associated beginLayerOp, since it has all the state needed for drawing
diff --git a/libs/hwui/OpReorderer.h b/libs/hwui/OpReorderer.h
index 927ecfa..77be402 100644
--- a/libs/hwui/OpReorderer.h
+++ b/libs/hwui/OpReorderer.h
@@ -32,6 +32,7 @@
class BakedOpState;
class BatchBase;
+class LayerUpdateQueue;
class MergingOpBatch;
class OffscreenBuffer;
class OpBatch;
@@ -64,9 +65,14 @@
*/
class LayerReorderer {
public:
+ // Create LayerReorderer for Fbo0
LayerReorderer(uint32_t width, uint32_t height)
- : width(width)
- , height(height) {}
+ : LayerReorderer(width, height, nullptr, nullptr) {};
+
+ // Create LayerReorderer for an offscreen layer, where beginLayerOp is present for a
+ // saveLayer, renderNode is present for a HW layer.
+ LayerReorderer(uint32_t width, uint32_t height,
+ const BeginLayerOp* beginLayerOp, RenderNode* renderNode);
// iterate back toward target to see if anything drawn since should overlap the new op
// if no target, merging ops still iterate to find similar batch to insert after
@@ -92,12 +98,12 @@
void dump() const;
- OffscreenBuffer* offscreenBuffer = nullptr;
- const BeginLayerOp* beginLayerOp = nullptr;
const uint32_t width;
const uint32_t height;
+ OffscreenBuffer* offscreenBuffer;
+ const BeginLayerOp* beginLayerOp;
+ const RenderNode* renderNode;
private:
-
std::vector<BatchBase*> mBatches;
/**
@@ -112,8 +118,8 @@
};
public:
- // TODO: not final, just presented this way for simplicity. Layers too?
- OpReorderer(const SkRect& clip, uint32_t viewportWidth, uint32_t viewportHeight,
+ OpReorderer(const LayerUpdateQueue& layers, const SkRect& clip,
+ uint32_t viewportWidth, uint32_t viewportHeight,
const std::vector< sp<RenderNode> >& nodes);
OpReorderer(int viewportWidth, int viewportHeight, const DisplayList& displayList);
@@ -144,8 +150,13 @@
// later in the list will be drawn by earlier ones
for (int i = mLayerReorderers.size() - 1; i >= 1; i--) {
LayerReorderer& layer = mLayerReorderers[i];
- if (!layer.empty()) {
- layer.offscreenBuffer = renderer.startLayer(layer.width, layer.height);
+ if (layer.renderNode) {
+ // cached HW layer - can't skip layer if empty
+ renderer.startLayer(layer.offscreenBuffer);
+ layer.replayBakedOpsImpl((void*)&renderer, receivers);
+ renderer.endLayer();
+ } else if (!layer.empty()) { // save layer - skip entire layer if empty
+ layer.offscreenBuffer = renderer.createLayer(layer.width, layer.height);
layer.replayBakedOpsImpl((void*)&renderer, receivers);
renderer.endLayer();
}
@@ -171,12 +182,19 @@
virtual GLuint getTargetFbo() const override { return 0; }
private:
+ void saveForLayer(uint32_t layerWidth, uint32_t layerHeight,
+ const BeginLayerOp* beginLayerOp, RenderNode* renderNode);
+ void restoreForLayer();
+
LayerReorderer& currentLayer() { return mLayerReorderers[mLayerStack.back()]; }
BakedOpState* tryBakeOpState(const RecordedOp& recordedOp) {
return BakedOpState::tryConstruct(mAllocator, *mCanvasState.currentSnapshot(), recordedOp);
}
+ // should always be surrounded by a save/restore pair
+ void deferNodePropsAndOps(RenderNode& node);
+
void deferImpl(const DisplayList& displayList);
void replayBakedOpsImpl(void* arg, BakedOpDispatcher* receivers);
diff --git a/libs/hwui/RecordedOp.h b/libs/hwui/RecordedOp.h
index 7874d85..9ae868a 100644
--- a/libs/hwui/RecordedOp.h
+++ b/libs/hwui/RecordedOp.h
@@ -20,6 +20,7 @@
#include "utils/LinearAllocator.h"
#include "Rect.h"
#include "Matrix.h"
+#include "RenderNode.h"
#include "SkXfermode.h"
@@ -136,13 +137,42 @@
: RecordedOp(RecordedOpId::EndLayerOp, Rect(0, 0), Matrix4::identity(), Rect(0, 0), nullptr) {}
};
+/**
+ * Draws an OffscreenBuffer.
+ *
+ * Alpha, mode, and colorfilter are embedded, since LayerOps are always dynamically generated,
+ * when creating/tracking a SkPaint* during defer isn't worth the bother.
+ */
struct LayerOp : RecordedOp {
+ // Records a one-use (saveLayer) layer for drawing. Once drawn, the layer will be destroyed.
LayerOp(BASE_PARAMS, OffscreenBuffer** layerHandle)
- : SUPER(LayerOp)
- , layerHandle(layerHandle) {}
+ : SUPER_PAINTLESS(LayerOp)
+ , layerHandle(layerHandle)
+ , alpha(paint->getAlpha() / 255.0f)
+ , mode(PaintUtils::getXfermodeDirect(paint))
+ , colorFilter(paint->getColorFilter())
+ , destroy(true) {}
+
+ LayerOp(RenderNode& node)
+ : RecordedOp(RecordedOpId::LayerOp, Rect(node.getWidth(), node.getHeight()), Matrix4::identity(), Rect(node.getWidth(), node.getHeight()), nullptr)
+ , layerHandle(node.getLayerHandle())
+ , alpha(node.properties().layerProperties().alpha() / 255.0f)
+ , mode(node.properties().layerProperties().xferMode())
+ , colorFilter(node.properties().layerProperties().colorFilter())
+ , destroy(false) {}
+
// Records a handle to the Layer object, since the Layer itself won't be
// constructed until after this operation is constructed.
OffscreenBuffer** layerHandle;
+ const float alpha;
+ const SkXfermode::Mode mode;
+
+ // pointer to object owned by either LayerProperties, or a recorded Paint object in a
+ // BeginLayerOp. Lives longer than LayerOp in either case, so no skia ref counting is used.
+ SkColorFilter* colorFilter;
+
+ // whether to destroy the layer, once rendered
+ const bool destroy;
};
}; // namespace uirenderer
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 273af3a..7c460b1 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -77,7 +77,6 @@
// ----------------------------------------------------------------------------
void RecordingCanvas::onViewportInitialized() {
-
}
void RecordingCanvas::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 454ee24..8a56475 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -26,9 +26,10 @@
#include "SkiaCanvasProxy.h"
#include "Snapshot.h"
-#include "SkDrawFilter.h"
-#include "SkPaint.h"
-#include "SkTLazy.h"
+#include <SkDrawFilter.h>
+#include <SkPaint.h>
+#include <SkTLazy.h>
+
#include <vector>
namespace android {
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 39cb8e9..0601944 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -20,6 +20,7 @@
#include "Debug.h"
#if HWUI_NEW_OPS
#include "RecordedOp.h"
+#include "BakedOpRenderer.h"
#endif
#include "DisplayListOp.h"
#include "LayerRenderer.h"
@@ -42,11 +43,15 @@
namespace uirenderer {
void RenderNode::debugDumpLayers(const char* prefix) {
+#if HWUI_NEW_OPS
+ LOG_ALWAYS_FATAL("TODO: dump layer");
+#else
if (mLayer) {
ALOGD("%sNode %p (%s) has layer %p (fbo = %u, wasBuildLayered = %s)",
prefix, this, getName(), mLayer, mLayer->getFbo(),
mLayer->wasBuildLayered ? "true" : "false");
}
+#endif
if (mDisplayList) {
for (auto&& child : mDisplayList->getChildren()) {
child->renderNode->debugDumpLayers(prefix);
@@ -60,18 +65,21 @@
, mDisplayList(nullptr)
, mStagingDisplayList(nullptr)
, mAnimatorManager(*this)
- , mLayer(nullptr)
, mParentCount(0) {
}
RenderNode::~RenderNode() {
deleteDisplayList();
delete mStagingDisplayList;
+#if HWUI_NEW_OPS
+ LOG_ALWAYS_FATAL_IF(mLayer, "layer missed detachment!");
+#else
if (mLayer) {
ALOGW("Memory Warning: Layer %p missed its detachment, held on to for far too long!", mLayer);
mLayer->postDecStrong();
mLayer = nullptr;
}
+#endif
}
void RenderNode::setStagingDisplayList(DisplayList* displayList) {
@@ -240,13 +248,29 @@
}
}
+layer_t* createLayer(RenderState& renderState, uint32_t width, uint32_t height) {
+#if HWUI_NEW_OPS
+ return BakedOpRenderer::createOffscreenBuffer(renderState, width, height);
+#else
+ return LayerRenderer::createRenderLayer(renderState, width, height);
+#endif
+}
+
+void destroyLayer(layer_t* layer) {
+#if HWUI_NEW_OPS
+ BakedOpRenderer::destroyOffscreenBuffer(layer);
+#else
+ LayerRenderer::destroyLayer(layer);
+#endif
+}
+
void RenderNode::pushLayerUpdate(TreeInfo& info) {
LayerType layerType = properties().effectiveLayerType();
// If we are not a layer OR we cannot be rendered (eg, view was detached)
// we need to destroy any Layers we may have had previously
if (CC_LIKELY(layerType != LayerType::RenderLayer) || CC_UNLIKELY(!isRenderable())) {
if (CC_UNLIKELY(mLayer)) {
- LayerRenderer::destroyLayer(mLayer);
+ destroyLayer(mLayer);
mLayer = nullptr;
}
return;
@@ -254,14 +278,18 @@
bool transformUpdateNeeded = false;
if (!mLayer) {
- mLayer = LayerRenderer::createRenderLayer(
- info.canvasContext.getRenderState(), getWidth(), getHeight());
- applyLayerPropertiesToLayer(info);
- damageSelf(info);
- transformUpdateNeeded = true;
+ mLayer = createLayer(info.canvasContext.getRenderState(), getWidth(), getHeight());
+ damageSelf(info);
+ transformUpdateNeeded = true;
+#if HWUI_NEW_OPS
+ } else if (mLayer->viewportWidth != getWidth() || mLayer->viewportHeight != getHeight()) {
+ // TODO: allow it to grow larger
+ if (getWidth() > mLayer->texture.width || getHeight() > mLayer->texture.height) {
+#else
} else if (mLayer->layer.getWidth() != getWidth() || mLayer->layer.getHeight() != getHeight()) {
if (!LayerRenderer::resizeLayer(mLayer, getWidth(), getHeight())) {
- LayerRenderer::destroyLayer(mLayer);
+#endif
+ destroyLayer(mLayer);
mLayer = nullptr;
}
damageSelf(info);
@@ -276,7 +304,7 @@
if (info.errorHandler) {
std::ostringstream err;
err << "Unable to create layer for " << getName();
- const int maxTextureSize = Caches::getInstance().maxTextureSize;
+ const uint32_t maxTextureSize = Caches::getInstance().maxTextureSize;
if (getWidth() > maxTextureSize || getHeight() > maxTextureSize) {
err << ", size " << getWidth() << "x" << getHeight()
<< " exceeds max size " << maxTextureSize;
@@ -292,9 +320,16 @@
// update the transform in window of the layer to reset its origin wrt light source position
Matrix4 windowTransform;
info.damageAccumulator->computeCurrentTransform(&windowTransform);
+#if HWUI_NEW_OPS
+ // TODO: update layer transform (perhaps as part of enqueueLayerWithDamage)
+#else
mLayer->setWindowTransform(windowTransform);
+#endif
}
+#if HWUI_NEW_OPS
+ info.layerUpdateQueue->enqueueLayerWithDamage(this, dirty);
+#else
if (dirty.intersect(0, 0, getWidth(), getHeight())) {
dirty.roundOut(&dirty);
mLayer->updateDeferred(this, dirty.fLeft, dirty.fTop, dirty.fRight, dirty.fBottom);
@@ -304,6 +339,7 @@
if (info.renderer && mLayer->deferredUpdateScheduled) {
info.renderer->pushLayerUpdate(mLayer);
}
+#endif
// There might be prefetched layers that need to be accounted for.
// That might be us, so tell CanvasContext that this layer is in the
@@ -365,7 +401,9 @@
damageSelf(info);
info.damageAccumulator->popTransform();
syncProperties();
+#if !HWUI_NEW_OPS
applyLayerPropertiesToLayer(info);
+#endif
// We could try to be clever and only re-damage if the matrix changed.
// However, we don't need to worry about that. The cost of over-damaging
// here is only going to be a single additional map rect of this node
@@ -376,6 +414,7 @@
}
}
+#if !HWUI_NEW_OPS
void RenderNode::applyLayerPropertiesToLayer(TreeInfo& info) {
if (CC_LIKELY(!mLayer)) return;
@@ -384,6 +423,7 @@
mLayer->setColorFilter(props.colorFilter());
mLayer->setBlend(props.needsBlending());
}
+#endif
void RenderNode::syncDisplayList() {
// Make sure we inc first so that we don't fluctuate between 0 and 1,
@@ -451,7 +491,7 @@
void RenderNode::destroyHardwareResources() {
if (mLayer) {
- LayerRenderer::destroyLayer(mLayer);
+ destroyLayer(mLayer);
mLayer = nullptr;
}
if (mDisplayList) {
@@ -978,7 +1018,11 @@
return;
}
+#if HWUI_NEW_OPS
+ const bool drawLayer = false;
+#else
const bool drawLayer = (mLayer && (&renderer != mLayer->renderer.get()));
+#endif
// If we are updating the contents of mLayer, we don't want to apply any of
// the RenderNode's properties to this issueOperations pass. Those will all
// be applied when the layer is drawn, aka when this is true.
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index 57e41c6..3500cb2 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -44,13 +44,22 @@
namespace uirenderer {
class CanvasState;
-class DisplayListOp;
class DisplayListCanvas;
+class DisplayListOp;
class OpenGLRenderer;
+class OpReorderer;
class Rect;
-class Layer;
class SkiaShader;
+
+#if HWUI_NEW_OPS
+class OffscreenBuffer;
+typedef OffscreenBuffer layer_t;
+#else
+class Layer;
+typedef Layer layer_t;
+#endif
+
class ClipRectOp;
class SaveLayerOp;
class SaveOp;
@@ -162,11 +171,11 @@
return mStagingProperties;
}
- int getWidth() {
+ uint32_t getWidth() {
return properties().getWidth();
}
- int getHeight() {
+ uint32_t getHeight() {
return properties().getHeight();
}
@@ -193,9 +202,13 @@
}
// Only call if RenderNode has DisplayList...
- const DisplayList& getDisplayList() const {
- return *mDisplayList;
+ const DisplayList* getDisplayList() const {
+ return mDisplayList;
}
+#if HWUI_NEW_OPS
+ OffscreenBuffer* getLayer() const { return mLayer; }
+ OffscreenBuffer** getLayerHandle() { return &mLayer; } // ugh...
+#endif
private:
typedef key_value_pair_t<float, DrawRenderNodeOp*> ZDrawRenderNodeOpPair;
@@ -262,7 +275,9 @@
void pushStagingPropertiesChanges(TreeInfo& info);
void pushStagingDisplayListChanges(TreeInfo& info);
void prepareSubTree(TreeInfo& info, bool functorsNeedLayer, DisplayList* subtree);
+#if !HWUI_NEW_OPS
void applyLayerPropertiesToLayer(TreeInfo& info);
+#endif
void prepareLayer(TreeInfo& info, uint32_t dirtyMask);
void pushLayerUpdate(TreeInfo& info);
void deleteDisplayList();
@@ -287,7 +302,7 @@
// Owned by RT. Lifecycle is managed by prepareTree(), with the exception
// being in ~RenderNode() which may happen on any thread.
- Layer* mLayer;
+ layer_t* mLayer = nullptr;
/**
* Draw time state - these properties are only set and used during rendering
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index 1c31487..be25516 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -16,11 +16,11 @@
#ifndef TREEINFO_H
#define TREEINFO_H
-#include <string>
+#include "utils/Macros.h"
#include <utils/Timers.h>
-#include "utils/Macros.h"
+#include <string>
namespace android {
namespace uirenderer {
@@ -30,6 +30,7 @@
}
class DamageAccumulator;
+class LayerUpdateQueue;
class OpenGLRenderer;
class RenderState;
@@ -75,9 +76,14 @@
// Must not be null during actual usage
DamageAccumulator* damageAccumulator = nullptr;
+
+#if HWUI_NEW_OPS
+ LayerUpdateQueue* layerUpdateQueue = nullptr;
+#else
// The renderer that will be drawing the next frame. Use this to push any
// layer updates or similar. May be NULL.
OpenGLRenderer* renderer = nullptr;
+#endif
ErrorHandler* errorHandler = nullptr;
struct Out {
diff --git a/libs/hwui/microbench/OpReordererBench.cpp b/libs/hwui/microbench/OpReordererBench.cpp
index 43f170f..7b8d0e5 100644
--- a/libs/hwui/microbench/OpReordererBench.cpp
+++ b/libs/hwui/microbench/OpReordererBench.cpp
@@ -56,7 +56,9 @@
BENCHMARK_NO_ARG(BM_OpReorderer_deferAndRender);
void BM_OpReorderer_deferAndRender::Run(int iters) {
- TestUtils::runOnRenderThread([this, iters](RenderState& renderState, Caches& caches) {
+ TestUtils::runOnRenderThread([this, iters](renderthread::RenderThread& thread) {
+ RenderState& renderState = thread.renderState();
+ Caches& caches = Caches::getInstance();
StartBenchmarkTiming();
for (int i = 0; i < iters; i++) {
OpReorderer reorderer(200, 200, *sReorderingDisplayList);
diff --git a/libs/hwui/renderstate/MeshState.cpp b/libs/hwui/renderstate/MeshState.cpp
index 0521f65..03cb5ce 100644
--- a/libs/hwui/renderstate/MeshState.cpp
+++ b/libs/hwui/renderstate/MeshState.cpp
@@ -100,6 +100,24 @@
return false;
}
+void MeshState::genOrUpdateMeshBuffer(GLuint* buffer, GLsizeiptr size,
+ const void* data, GLenum usage) {
+ if (!*buffer) {
+ glGenBuffers(1, buffer);
+ }
+ bindMeshBuffer(*buffer);
+ glBufferData(GL_ARRAY_BUFFER, size, data, usage);
+}
+
+void MeshState::deleteMeshBuffer(GLuint buffer) {
+ if (buffer == mCurrentBuffer) {
+ // GL defines that deleting the currently bound VBO rebinds to 0 (no VBO).
+ // Reflect this in our cached value.
+ mCurrentBuffer = 0;
+ }
+ glDeleteBuffers(1, &buffer);
+}
+
///////////////////////////////////////////////////////////////////////////////
// Vertices
///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/renderstate/MeshState.h b/libs/hwui/renderstate/MeshState.h
index e80f4d0..6c0fb78 100644
--- a/libs/hwui/renderstate/MeshState.h
+++ b/libs/hwui/renderstate/MeshState.h
@@ -75,6 +75,9 @@
*/
bool unbindMeshBuffer();
+ void genOrUpdateMeshBuffer(GLuint* buffer, GLsizeiptr size, const void* data, GLenum usage);
+ void deleteMeshBuffer(GLuint);
+
///////////////////////////////////////////////////////////////////////////////
// Vertices
///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index c1f6670..b6fecb4 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -20,6 +20,7 @@
#include "Caches.h"
#include "DeferredLayerUpdater.h"
#include "EglManager.h"
+#include "LayerUpdateQueue.h"
#include "LayerRenderer.h"
#include "OpenGLRenderer.h"
#include "Properties.h"
@@ -198,7 +199,11 @@
mCurrentFrameInfo->markSyncStart();
info.damageAccumulator = &mDamageAccumulator;
+#if HWUI_NEW_OPS
+ info.layerUpdateQueue = &mLayerUpdateQueue;
+#else
info.renderer = mCanvas;
+#endif
mAnimationContext->startFrame(info.mode);
for (const sp<RenderNode>& node : mRenderNodes) {
@@ -333,7 +338,8 @@
mEglManager.damageFrame(frame, dirty);
#if HWUI_NEW_OPS
- OpReorderer reorderer(dirty, frame.width(), frame.height(), mRenderNodes);
+ OpReorderer reorderer(mLayerUpdateQueue, dirty, frame.width(), frame.height(), mRenderNodes);
+ mLayerUpdateQueue.clear();
BakedOpRenderer renderer(Caches::getInstance(), mRenderThread.renderState(), mOpaque);
// TODO: profiler().draw(mCanvas);
reorderer.replayBakedOps<BakedOpDispatcher>(renderer);
@@ -555,7 +561,11 @@
TreeInfo info(TreeInfo::MODE_FULL, *this);
info.damageAccumulator = &mDamageAccumulator;
+#if HWUI_NEW_OPS
+ info.layerUpdateQueue = &mLayerUpdateQueue;
+#else
info.renderer = mCanvas;
+#endif
info.runAnimations = false;
node->prepareTree(info);
SkRect ignore;
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 30e6562..d656014 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -18,9 +18,10 @@
#define CANVASCONTEXT_H_
#include "DamageAccumulator.h"
-#include "IContextFactory.h"
#include "FrameInfo.h"
#include "FrameInfoVisualizer.h"
+#include "IContextFactory.h"
+#include "LayerUpdateQueue.h"
#include "RenderNode.h"
#include "utils/RingBuffer.h"
#include "renderthread/RenderTask.h"
@@ -83,7 +84,7 @@
void draw();
void destroy();
- // IFrameCallback, Chroreographer-driven frame callback entry point
+ // IFrameCallback, Choreographer-driven frame callback entry point
virtual void doFrame() override;
void prepareAndDraw(RenderNode* node);
@@ -118,7 +119,7 @@
void addRenderNode(RenderNode* node, bool placeFront) {
int pos = placeFront ? 0 : static_cast<int>(mRenderNodes.size());
- mRenderNodes.emplace( mRenderNodes.begin() + pos, node);
+ mRenderNodes.emplace(mRenderNodes.begin() + pos, node);
}
void removeRenderNode(RenderNode* node) {
@@ -166,6 +167,7 @@
OpenGLRenderer* mCanvas = nullptr;
bool mHaveNewSurface = false;
DamageAccumulator mDamageAccumulator;
+ LayerUpdateQueue mLayerUpdateQueue;
std::unique_ptr<AnimationContext> mAnimationContext;
std::vector< sp<RenderNode> > mRenderNodes;
diff --git a/libs/hwui/tests/TreeContentAnimation.cpp b/libs/hwui/tests/TreeContentAnimation.cpp
index 2eefd37..29d9803 100644
--- a/libs/hwui/tests/TreeContentAnimation.cpp
+++ b/libs/hwui/tests/TreeContentAnimation.cpp
@@ -24,6 +24,7 @@
#include <RenderNode.h>
#include <renderthread/RenderProxy.h>
#include <renderthread/RenderTask.h>
+#include <unit_tests/TestUtils.h>
#include "Benchmark.h"
#include "TestContext.h"
@@ -401,3 +402,27 @@
"Tests the clipped saveLayer codepath. Draws content into offscreen buffers and back again.",
TreeContentAnimation::run<SaveLayerAnimation>
});
+
+
+class HwLayerAnimation : public TreeContentAnimation {
+public:
+ sp<RenderNode> card = TestUtils::createNode<TestCanvas>(0, 0, 200, 200, [] (TestCanvas& canvas) {
+ canvas.drawColor(0xFF0000FF, SkXfermode::kSrcOver_Mode);
+ }, true);
+ void createContent(int width, int height, TestCanvas* canvas) override {
+ canvas->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); // background
+ canvas->drawRenderNode(card.get());
+ }
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 150;
+ card->mutateStagingProperties().setTranslationX(curFrame);
+ card->mutateStagingProperties().setTranslationY(curFrame);
+ card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ }
+};
+static Benchmark _HwLayer(BenchmarkInfo{
+ "hwlayer",
+ "A nested pair of nodes with LAYER_TYPE_HARDWARE set on each. "
+ "Tests the hardware layer codepath.",
+ TreeContentAnimation::run<HwLayerAnimation>
+});
diff --git a/libs/hwui/unit_tests/FatVectorTests.cpp b/libs/hwui/unit_tests/FatVectorTests.cpp
index fb760ac5..3ef329a 100644
--- a/libs/hwui/unit_tests/FatVectorTests.cpp
+++ b/libs/hwui/unit_tests/FatVectorTests.cpp
@@ -56,6 +56,27 @@
}
}
+TEST(FatVector, preSizeConstructor) {
+ {
+ FatVector<int, 4> v(32);
+ EXPECT_EQ(32u, v.capacity());
+ EXPECT_EQ(32u, v.size());
+ EXPECT_FALSE(allocationIsInternal(v));
+ }
+ {
+ FatVector<int, 4> v(4);
+ EXPECT_EQ(4u, v.capacity());
+ EXPECT_EQ(4u, v.size());
+ EXPECT_TRUE(allocationIsInternal(v));
+ }
+ {
+ FatVector<int, 4> v(2);
+ EXPECT_EQ(4u, v.capacity());
+ EXPECT_EQ(2u, v.size());
+ EXPECT_TRUE(allocationIsInternal(v));
+ }
+}
+
TEST(FatVector, shrink) {
FatVector<int, 10> v;
EXPECT_TRUE(allocationIsInternal(v));
@@ -78,9 +99,9 @@
FatVector<TestUtils::SignalingDtor, 0> v;
v.emplace_back(&count);
EXPECT_FALSE(allocationIsInternal(v));
- EXPECT_EQ(0, count);
+ EXPECT_EQ(0, count) << "Destruction shouldn't have happened yet";
}
- EXPECT_EQ(1, count);
+ EXPECT_EQ(1, count) << "Destruction should happen exactly once";
}
TEST(FatVector, destructorExternal) {
@@ -92,7 +113,7 @@
v.emplace_back(&count);
EXPECT_TRUE(allocationIsInternal(v));
}
- EXPECT_EQ(0, count);
+ EXPECT_EQ(0, count) << "Destruction shouldn't have happened yet";
}
- EXPECT_EQ(10, count);
+ EXPECT_EQ(10, count) << "Destruction should happen exactly once";
}
diff --git a/libs/hwui/unit_tests/LayerUpdateQueueTests.cpp b/libs/hwui/unit_tests/LayerUpdateQueueTests.cpp
new file mode 100644
index 0000000..9d625bc
--- /dev/null
+++ b/libs/hwui/unit_tests/LayerUpdateQueueTests.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2015 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 <gtest/gtest.h>
+
+#include <LayerUpdateQueue.h>
+#include <RenderNode.h>
+
+#include <unit_tests/TestUtils.h>
+
+namespace android {
+namespace uirenderer {
+
+TEST(LayerUpdateQueue, construct) {
+ LayerUpdateQueue queue;
+ EXPECT_TRUE(queue.entries().empty());
+}
+
+// sync node properties, so properties() reflects correct width and height
+static sp<RenderNode> createSyncedNode(uint32_t width, uint32_t height) {
+ sp<RenderNode> node = TestUtils::createNode(0, 0, width, height);
+ TestUtils::syncNodePropertiesAndDisplayList(node);
+ return node;
+}
+
+TEST(LayerUpdateQueue, enqueueSimple) {
+ sp<RenderNode> a = createSyncedNode(100, 100);
+ sp<RenderNode> b = createSyncedNode(200, 200);
+
+ LayerUpdateQueue queue;
+ queue.enqueueLayerWithDamage(a.get(), Rect(25, 25, 75, 75));
+ queue.enqueueLayerWithDamage(b.get(), Rect(100, 100, 300, 300));
+
+ EXPECT_EQ(2u, queue.entries().size());
+
+ EXPECT_EQ(a.get(), queue.entries()[0].renderNode);
+ EXPECT_EQ(Rect(25, 25, 75, 75), queue.entries()[0].damage);
+ EXPECT_EQ(b.get(), queue.entries()[1].renderNode);
+ EXPECT_EQ(Rect(100, 100, 200, 200), queue.entries()[1].damage); // clipped to bounds
+}
+
+TEST(LayerUpdateQueue, enqueueUnion) {
+ sp<RenderNode> a = createSyncedNode(100, 100);
+
+ LayerUpdateQueue queue;
+ queue.enqueueLayerWithDamage(a.get(), Rect(10, 10, 20, 20));
+ queue.enqueueLayerWithDamage(a.get(), Rect(30, 30, 40, 40));
+
+ EXPECT_EQ(1u, queue.entries().size());
+
+ EXPECT_EQ(a.get(), queue.entries()[0].renderNode);
+ EXPECT_EQ(Rect(10, 10, 40, 40), queue.entries()[0].damage);
+}
+
+TEST(LayerUpdateQueue, clear) {
+ sp<RenderNode> a = createSyncedNode(100, 100);
+
+ LayerUpdateQueue queue;
+ queue.enqueueLayerWithDamage(a.get(), Rect(100, 100));
+
+ EXPECT_FALSE(queue.entries().empty());
+
+ queue.clear();
+
+ EXPECT_TRUE(queue.entries().empty());
+}
+
+};
+};
diff --git a/libs/hwui/unit_tests/OpReordererTests.cpp b/libs/hwui/unit_tests/OpReordererTests.cpp
index ffb575f..09b10c3 100644
--- a/libs/hwui/unit_tests/OpReordererTests.cpp
+++ b/libs/hwui/unit_tests/OpReordererTests.cpp
@@ -20,6 +20,7 @@
#include <OpReorderer.h>
#include <RecordedOp.h>
#include <RecordingCanvas.h>
+#include <renderthread/CanvasContext.h> // todo: remove
#include <unit_tests/TestUtils.h>
#include <unordered_map>
@@ -27,6 +28,7 @@
namespace android {
namespace uirenderer {
+LayerUpdateQueue sEmptyLayerUpdateQueue;
/**
* Virtual class implemented by each test to redirect static operation / state transitions to
@@ -42,14 +44,24 @@
class TestRendererBase {
public:
virtual ~TestRendererBase() {}
- virtual OffscreenBuffer* startLayer(uint32_t width, uint32_t height) { ADD_FAILURE(); return nullptr; }
- virtual void endLayer() { ADD_FAILURE(); }
+ virtual OffscreenBuffer* createLayer(uint32_t, uint32_t) {
+ ADD_FAILURE() << "Layer creation not expected in this test";
+ return nullptr;
+ }
+ virtual void startLayer(OffscreenBuffer*) {
+ ADD_FAILURE() << "Layer repaint not expected in this test";
+ }
+ virtual void endLayer() {
+ ADD_FAILURE() << "Layer updates not expected in this test";
+ }
virtual void startFrame(uint32_t width, uint32_t height) {}
virtual void endFrame() {}
// define virtual defaults for direct
#define BASE_OP_METHOD(Type) \
- virtual void on##Type(const Type&, const BakedOpState&) { ADD_FAILURE(); }
+ virtual void on##Type(const Type&, const BakedOpState&) { \
+ ADD_FAILURE() << #Type " not expected in this test"; \
+ }
MAP_OPS(BASE_OP_METHOD)
int getIndex() { return mIndex; }
@@ -192,7 +204,8 @@
std::vector< sp<RenderNode> > nodes;
nodes.push_back(parent.get());
- OpReorderer reorderer(SkRect::MakeWH(200, 200), 200, 200, nodes);
+ OpReorderer reorderer(sEmptyLayerUpdateQueue,
+ SkRect::MakeWH(200, 200), 200, 200, nodes);
RenderNodeTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
@@ -216,7 +229,8 @@
std::vector< sp<RenderNode> > nodes;
nodes.push_back(node.get());
- OpReorderer reorderer(SkRect::MakeLTRB(10, 20, 30, 40), // clip to small area, should see in receiver
+ OpReorderer reorderer(sEmptyLayerUpdateQueue,
+ SkRect::MakeLTRB(10, 20, 30, 40), // clip to small area, should see in receiver
200, 200, nodes);
ClippedTestRenderer renderer;
@@ -226,7 +240,7 @@
class SaveLayerSimpleTestRenderer : public TestRendererBase {
public:
- OffscreenBuffer* startLayer(uint32_t width, uint32_t height) override {
+ OffscreenBuffer* createLayer(uint32_t width, uint32_t height) override {
EXPECT_EQ(0, mIndex++);
EXPECT_EQ(180u, width);
EXPECT_EQ(180u, height);
@@ -268,13 +282,13 @@
/* saveLayer1 {rect1, saveLayer2 { rect2 } } will play back as:
- * - startLayer2, rect2 endLayer2
- * - startLayer1, rect1, drawLayer2, endLayer1
+ * - createLayer2, rect2 endLayer2
+ * - createLayer1, rect1, drawLayer2, endLayer1
* - startFrame, layerOp1, endFrame
*/
class SaveLayerNestedTestRenderer : public TestRendererBase {
public:
- OffscreenBuffer* startLayer(uint32_t width, uint32_t height) override {
+ OffscreenBuffer* createLayer(uint32_t width, uint32_t height) override {
const int index = mIndex++;
if (index == 0) {
EXPECT_EQ(400u, width);
@@ -356,5 +370,162 @@
reorderer.replayBakedOps<TestDispatcher>(renderer);
}
+class HwLayerSimpleTestRenderer : public TestRendererBase {
+public:
+ void startLayer(OffscreenBuffer* offscreenBuffer) override {
+ EXPECT_EQ(0, mIndex++);
+ EXPECT_EQ(offscreenBuffer, (OffscreenBuffer*) 0x0124);
+ }
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(1, mIndex++);
+
+ EXPECT_TRUE(state.computedState.transform.isIdentity())
+ << "Transform should be reset within layer";
+
+ EXPECT_EQ(state.computedState.clipRect, Rect(25, 25, 75, 75))
+ << "Damage rect should be used to clip layer content";
+ }
+ void endLayer() override {
+ EXPECT_EQ(2, mIndex++);
+ }
+ void startFrame(uint32_t width, uint32_t height) override {
+ EXPECT_EQ(3, mIndex++);
+ }
+ void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(4, mIndex++);
+ }
+ void endFrame() override {
+ EXPECT_EQ(5, mIndex++);
+ }
+};
+TEST(OpReorderer, hwLayerSimple) {
+ sp<RenderNode> node = TestUtils::createNode<RecordingCanvas>(10, 10, 110, 110, [](RecordingCanvas& canvas) {
+ SkPaint paint;
+ paint.setColor(SK_ColorWHITE);
+ canvas.drawRect(0, 0, 100, 100, paint);
+ });
+ node->mutateStagingProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
+ node->setPropertyFieldsDirty(RenderNode::GENERIC);
+ OffscreenBuffer** bufferHandle = node->getLayerHandle();
+ *bufferHandle = (OffscreenBuffer*) 0x0124;
+
+ TestUtils::syncNodePropertiesAndDisplayList(node);
+
+ std::vector< sp<RenderNode> > nodes;
+ nodes.push_back(node.get());
+
+ // only enqueue partial damage
+ LayerUpdateQueue layerUpdateQueue;
+ layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75));
+
+ OpReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, nodes);
+
+ HwLayerSimpleTestRenderer renderer;
+ reorderer.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(6, renderer.getIndex());
+
+ // clean up layer pointer, so we can safely destruct RenderNode
+ *bufferHandle = nullptr;
+}
+
+
+/* parentLayer { greyRect, saveLayer { childLayer { whiteRect } } } will play back as:
+ * - startLayer(child), rect(grey), endLayer
+ * - createLayer, drawLayer(child), endLayer
+ * - startLayer(parent), rect(white), drawLayer(saveLayer), endLayer
+ * - startFrame, drawLayer(parent), endLayerb
+ */
+class HwLayerComplexTestRenderer : public TestRendererBase {
+public:
+ OffscreenBuffer* createLayer(uint32_t width, uint32_t height) {
+ EXPECT_EQ(3, mIndex++); // savelayer first
+ return (OffscreenBuffer*)0xabcd;
+ }
+ void startLayer(OffscreenBuffer* offscreenBuffer) override {
+ int index = mIndex++;
+ if (index == 0) {
+ // starting inner layer
+ EXPECT_EQ((OffscreenBuffer*)0x4567, offscreenBuffer);
+ } else if (index == 6) {
+ // starting outer layer
+ EXPECT_EQ((OffscreenBuffer*)0x0123, offscreenBuffer);
+ } else { ADD_FAILURE(); }
+ }
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ int index = mIndex++;
+ if (index == 1) {
+ // inner layer's rect (white)
+ EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
+ } else if (index == 7) {
+ // outer layer's rect (grey)
+ EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
+ } else { ADD_FAILURE(); }
+ }
+ void endLayer() override {
+ int index = mIndex++;
+ EXPECT_TRUE(index == 2 || index == 5 || index == 9);
+ }
+ void startFrame(uint32_t width, uint32_t height) override {
+ EXPECT_EQ(10, mIndex++);
+ }
+ void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
+ int index = mIndex++;
+ if (index == 4) {
+ EXPECT_EQ((OffscreenBuffer*)0x4567, *op.layerHandle);
+ } else if (index == 8) {
+ EXPECT_EQ((OffscreenBuffer*)0xabcd, *op.layerHandle);
+ } else if (index == 11) {
+ EXPECT_EQ((OffscreenBuffer*)0x0123, *op.layerHandle);
+ } else { ADD_FAILURE(); }
+ }
+ void endFrame() override {
+ EXPECT_EQ(12, mIndex++);
+ }
+};
+TEST(OpReorderer, hwLayerComplex) {
+ sp<RenderNode> child = TestUtils::createNode<RecordingCanvas>(50, 50, 150, 150, [](RecordingCanvas& canvas) {
+ SkPaint paint;
+ paint.setColor(SK_ColorWHITE);
+ canvas.drawRect(0, 0, 100, 100, paint);
+ });
+ child->mutateStagingProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
+ child->setPropertyFieldsDirty(RenderNode::GENERIC);
+ *(child->getLayerHandle()) = (OffscreenBuffer*) 0x4567;
+
+ RenderNode* childPtr = child.get();
+ sp<RenderNode> parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, [childPtr](RecordingCanvas& canvas) {
+ SkPaint paint;
+ paint.setColor(SK_ColorDKGRAY);
+ canvas.drawRect(0, 0, 200, 200, paint);
+
+ canvas.saveLayerAlpha(50, 50, 150, 150, 128, SkCanvas::kClipToLayer_SaveFlag);
+ canvas.drawRenderNode(childPtr);
+ canvas.restore();
+ });
+ parent->mutateStagingProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
+ parent->setPropertyFieldsDirty(RenderNode::GENERIC);
+ *(parent->getLayerHandle()) = (OffscreenBuffer*) 0x0123;
+
+ TestUtils::syncNodePropertiesAndDisplayList(child);
+ TestUtils::syncNodePropertiesAndDisplayList(parent);
+
+ std::vector< sp<RenderNode> > nodes;
+ nodes.push_back(parent.get());
+
+ LayerUpdateQueue layerUpdateQueue;
+ layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(100, 100));
+ layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(200, 200));
+
+ OpReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, nodes);
+
+ HwLayerComplexTestRenderer renderer;
+ reorderer.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(13, renderer.getIndex());
+
+ // clean up layer pointers, so we can safely destruct RenderNodes
+ *(child->getLayerHandle()) = nullptr;
+ *(parent->getLayerHandle()) = nullptr;
+}
+
} // namespace uirenderer
} // namespace android
diff --git a/libs/hwui/unit_tests/RecordingCanvasTests.cpp b/libs/hwui/unit_tests/RecordingCanvasTests.cpp
index e8cdf46..dcf1f64 100644
--- a/libs/hwui/unit_tests/RecordingCanvasTests.cpp
+++ b/libs/hwui/unit_tests/RecordingCanvasTests.cpp
@@ -53,7 +53,7 @@
ASSERT_EQ(Rect(0, 0, 100, 200), op.localClipRect);
ASSERT_EQ(Rect(10, 20, 90, 180), op.unmappedBounds);
});
- ASSERT_EQ(1, count); // only one observed
+ ASSERT_EQ(1, count);
}
TEST(RecordingCanvas, backgroundAndImage) {
@@ -106,7 +106,7 @@
}
count++;
});
- ASSERT_EQ(2, count); // two draws observed
+ ASSERT_EQ(2, count);
}
TEST(RecordingCanvas, saveLayerSimple) {
@@ -121,7 +121,9 @@
switch(count++) {
case 0:
EXPECT_EQ(RecordedOpId::BeginLayerOp, op.opId);
- // TODO: add asserts
+ EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds);
+ EXPECT_EQ(Rect(0, 0, 200, 200), op.localClipRect);
+ EXPECT_TRUE(op.localMatrix.isIdentity());
break;
case 1:
EXPECT_EQ(RecordedOpId::RectOp, op.opId);
@@ -132,7 +134,7 @@
break;
case 2:
EXPECT_EQ(RecordedOpId::EndLayerOp, op.opId);
- // TODO: add asserts
+ // Don't bother asserting recording state data - it's not used
break;
default:
ADD_FAILURE();
@@ -155,10 +157,8 @@
if (count++ == 1) {
Matrix4 expectedMatrix;
EXPECT_EQ(RecordedOpId::RectOp, op.opId);
-
- // recorded clip rect should be intersection of
- // viewport and saveLayer bounds, in layer space
- EXPECT_EQ(Rect(0, 0, 100, 100), op.localClipRect);
+ EXPECT_EQ(Rect(0, 0, 100, 100), op.localClipRect) << "Recorded clip rect should be"
+ " intersection of viewport and saveLayer bounds, in layer space";
EXPECT_EQ(Rect(0, 0, 400, 400), op.unmappedBounds);
expectedMatrix.loadTranslate(-100, -100, 0);
EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
@@ -183,14 +183,11 @@
int count = 0;
playbackOps(*dl, [&count](const RecordedOp& op) {
if (count++ == 1) {
- Matrix4 expectedMatrix;
EXPECT_EQ(RecordedOpId::RectOp, op.opId);
-
- // recorded rect doesn't see rotate, since recorded relative to saveLayer bounds
EXPECT_EQ(Rect(0, 0, 100, 100), op.localClipRect);
EXPECT_EQ(Rect(0, 0, 100, 100), op.unmappedBounds);
- expectedMatrix.loadIdentity();
- EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
+ EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), op.localMatrix)
+ << "Recorded op shouldn't see any canvas transform before the saveLayer";
}
});
EXPECT_EQ(3, count);
diff --git a/libs/hwui/unit_tests/TestUtils.h b/libs/hwui/unit_tests/TestUtils.h
index 5b09fda..770f413 100644
--- a/libs/hwui/unit_tests/TestUtils.h
+++ b/libs/hwui/unit_tests/TestUtils.h
@@ -89,15 +89,24 @@
return std::unique_ptr<DisplayList>(canvas.finishRecording());
}
- template<class CanvasType>
- static sp<RenderNode> createNode(int left, int top, int right, int bottom,
- std::function<void(CanvasType& canvas)> canvasCallback) {
+ static sp<RenderNode> createNode(int left, int top, int right, int bottom, bool onLayer = false) {
sp<RenderNode> node = new RenderNode();
node->mutateStagingProperties().setLeftTopRightBottom(left, top, right, bottom);
node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ if (onLayer) {
+ node->mutateStagingProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
+ node->setPropertyFieldsDirty(RenderNode::GENERIC);
+ }
+ return node;
+ }
- CanvasType canvas(
- node->stagingProperties().getWidth(), node->stagingProperties().getHeight());
+ template<class CanvasType>
+ static sp<RenderNode> createNode(int left, int top, int right, int bottom,
+ std::function<void(CanvasType& canvas)> canvasCallback, bool onLayer = false) {
+ sp<RenderNode> node = createNode(left, top, right, bottom, onLayer);
+
+ auto&& props = node->stagingProperties(); // staging, since not sync'd yet
+ CanvasType canvas(props.getWidth(), props.getHeight());
canvasCallback(canvas);
node->setStagingDisplayList(canvas.finishRecording());
return node;
@@ -108,7 +117,7 @@
node->syncDisplayList();
}
- typedef std::function<void(RenderState& state, Caches& caches)> RtCallback;
+ typedef std::function<void(renderthread::RenderThread& thread)> RtCallback;
class TestTask : public renderthread::RenderTask {
public:
@@ -120,7 +129,7 @@
RenderState& renderState = renderthread::RenderThread::getInstance().renderState();
renderState.onGLContextCreated();
- rtCallback(renderState, Caches::getInstance());
+ rtCallback(renderthread::RenderThread::getInstance());
renderState.onGLContextDestroyed();
};
RtCallback rtCallback;
diff --git a/libs/hwui/utils/FatVector.h b/libs/hwui/utils/FatVector.h
index c3c16c5a..315c249 100644
--- a/libs/hwui/utils/FatVector.h
+++ b/libs/hwui/utils/FatVector.h
@@ -91,6 +91,10 @@
InlineStdAllocator<T, SIZE>(mAllocation)) {
this->reserve(SIZE);
}
+
+ FatVector(size_t capacity) : FatVector() {
+ this->resize(capacity);
+ }
private:
typename InlineStdAllocator<T, SIZE>::Allocation mAllocation;
};
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index 7f22b8a..45529ef 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -221,8 +221,9 @@
* @return An array of audio encodings (e.g. {@link AudioFormat#ENCODING_PCM_16BIT},
* {@link AudioFormat#ENCODING_PCM_FLOAT}) supported by the audio device.
* <code>ENCODING_PCM_FLOAT</code> indicates the device supports more
- * than 16 bits of integer precision. Specifying <code>ENCODING_PCM_FLOAT</code>
- * with {@link AudioTrack} or {@link AudioRecord} can preserve at least 24 bits of
+ * than 16 bits of integer precision. As there is no AudioFormat constant
+ * specifically defined for 24-bit PCM, the value <code>ENCODING_PCM_FLOAT</code>
+ * indicates that {@link AudioTrack} or {@link AudioRecord} can preserve at least 24 bits of
* integer precision to that device.
*
* @see AudioFormat
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 875e716..50df556 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -3199,8 +3199,10 @@
/**
* Returns the value of the property with the specified key.
* @param key One of the strings corresponding to a property key: either
- * {@link #PROPERTY_OUTPUT_SAMPLE_RATE} or
- * {@link #PROPERTY_OUTPUT_FRAMES_PER_BUFFER}
+ * {@link #PROPERTY_OUTPUT_SAMPLE_RATE},
+ * {@link #PROPERTY_OUTPUT_FRAMES_PER_BUFFER},
+ * {@link #PROPERTY_SUPPORT_MIC_NEAR_ULTRASOUND}, or
+ * {@link #PROPERTY_SUPPORT_SPEAKER_NEAR_ULTRASOUND}.
* @return A string representing the associated value for that property key,
* or null if there is no value for that key.
*/
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index c44a7960..14eb084 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -231,4 +231,113 @@
<!-- Launch defaults preference summary with none set [CHAR LIMIT=40] -->
<string name="launch_defaults_none">No defaults set</string>
+ <!-- Text-To-Speech (TTS) settings --><skip />
+ <!-- Name of the TTS package as listed by the package manager. -->
+ <string name="tts_settings">Text-to-speech settings</string>
+ <!-- TTS option item name in the main settings screen -->
+ <string name="tts_settings_title">Text-to-speech output</string>
+ <!-- On main TTS Settings screen, in default settings section, setting default speech rate for synthesized voice -->
+ <string name="tts_default_rate_title">Speech rate</string>
+ <!-- On main TTS Settings screen, summary for default speech rate for synthesized voice -->
+ <string name="tts_default_rate_summary">Speed at which the text is spoken</string>
+ <!-- On main TTS Settings screen, in default settings section, setting default language for synthesized voice -->
+ <string name="tts_default_lang_title">Language</string>
+ <!-- Entry in the TTS engine language/locale picker, when selected will try to default to the system language [CHAR LIMIT=50] -->
+ <string name="tts_lang_use_system">Use system language</string>
+ <!-- On main TTS Settings screen, language summary if it can't default to system language [CHAR LIMIT=50] -->
+ <string name="tts_lang_not_selected">Language not selected</string>
+ <!-- On main TTS Settings screen, summary for default language for synthesized voice -->
+ <string name="tts_default_lang_summary">Sets the language-specific voice for the spoken text</string>
+ <!-- On main TTS Settings screen, triggers playback of an example of speech synthesis -->
+ <string name="tts_play_example_title">Listen to an example</string>
+ <!-- On main TTS Settings screen, summary for triggering playback of an example of speech synthesis -->
+ <string name="tts_play_example_summary">Play a short demonstration of speech synthesis</string>
+ <!-- On main TTS Settings screen, click to install required speech synthesis data -->
+ <string name="tts_install_data_title">Install voice data</string>
+ <!-- On main TTS Settings screen, summary for click to install required speech synthesis data -->
+ <string name="tts_install_data_summary">Install the voice data required for speech synthesis</string>
+ <!-- Warning message about security implications of enabling a TTS engine, displayed as a dialog
+ message when the user selects to enable an engine. -->
+ <string name="tts_engine_security_warning">This speech synthesis engine may be able to collect
+ all the text that will be spoken, including personal data like passwords and credit
+ card numbers. It comes from the <xliff:g id="tts_plugin_engine_name">%s</xliff:g> engine.
+ Enable the use of this speech synthesis engine?</string>
+ <!-- Warning message about required internet conectivity for TTS synthesis, displayed as a dialog
+ message when the user selects to play an example for network only locale and there's no internet connectivity. -->
+ <string name="tts_engine_network_required">This language requires a working network connection for text-to-speech output.</string>
+ <!-- Text spoken by the TTS engine as an example if the engine doesn't provide sample text [CHAR LIMIT=100] -->
+ <string name="tts_default_sample_string">This is an example of speech synthesis</string>
+ <!-- On main TTS Settings screen, title of a field explaining current TTS engine status for
+ current default language [CHAR LIMIT=50] -->
+ <string name="tts_status_title">Default language status</string>
+ <!-- On main TTS Settings screen, current TTS engine status for the current default language,
+ selected language is fully supported by the engine [CHAR LIMIT=150]-->
+ <string name="tts_status_ok"><xliff:g id="locale" example="English (United States)">%1$s</xliff:g> is fully supported</string>
+ <!-- On main TTS Settings screen, current TTS engine status for the current default language,
+ selected language is supported by the engine only if there's a working network connection [CHAR LIMIT=150] -->
+ <string name="tts_status_requires_network"><xliff:g id="locale" example="English (United States)">%1$s</xliff:g> requires network connection</string>
+ <!-- On main TTS Settings screen, current TTS engine status for the current default language,
+ selected language is not supported by the engine [CHAR LIMIT=150] -->
+ <string name="tts_status_not_supported"><xliff:g id="locale" example="English (United States)">%1$s</xliff:g> is not supported</string>
+ <!-- On main TTS Settings screen, current TTS engine status for the current default language,
+ tts engine is queried for status [CHAR LIMIT=150] -->
+ <string name="tts_status_checking">Checking…</string>
+ <!-- Title for a preference in the main TTS settings screen, which
+ launches the settings screen for a given TTS engine when clicked
+ [CHAR LIMIT=30]-->
+ <string name="tts_engine_settings_title">Settings for <xliff:g id="tts_engine_name">%s</xliff:g></string>
+ <!-- [CHAR LIMIT=150] Text for screen readers / accessibility programs for
+ the image that launches the TTS engine settings when clicked. -->
+ <string name="tts_engine_settings_button">Launch engine settings</string>
+ <!-- [CHAR LIMIT=50] The text for the settings section that users to set a
+ preferred text to speech engine -->
+ <string name="tts_engine_preference_section_title">Preferred engine</string>
+ <!-- [CHAR LIMIT=50] Title of the settings section that displays general preferences
+ that are applicable to all engines, such as the speech rate -->
+ <string name="tts_general_section_title">General</string>
+
+ <!-- Default speech rate choices -->
+ <string-array name="tts_rate_entries">
+ <item>Very slow</item>
+ <item>Slow</item>
+ <item>Normal</item>
+ <item>Fast</item>
+ <item>Faster</item>
+ <item>Very fast</item>
+ <item>Rapid</item>
+ <item>Very rapid</item>
+ <item>Fastest</item>
+ </string-array>
+ <!-- Do not translate. -->
+ <string-array name="tts_rate_values">
+ <item>60</item>
+ <item>80</item>
+ <item>100</item>
+ <item>150</item>
+ <item>200</item>
+ <item>250</item>
+ <item>300</item>
+ <item>350</item>
+ <item>400</item>
+ </string-array>
+
+ <!-- Do not translate. -->
+ <string-array name="tts_demo_strings" translatable="false">
+ <item>This is an example of speech synthesis in English.</item>
+ <item>Voici un échantillon de synthèse vocale en français.</item>
+ <item>Dies ist ein Beispiel für Sprachsynthese in Deutsch.</item>
+ <item>Questo è un esempio di sintesi vocale in italiano.</item>
+ <item>Este es un ejemplo de síntesis de voz en español.</item>
+ <item>이것은 한국어 음성 합성의 예입니다.</item>
+ </string-array>
+ <!-- Do not translate. -->
+ <string-array name="tts_demo_string_langs" translatable="false">
+ <item>eng</item>
+ <item>fra</item>
+ <item>deu</item>
+ <item>ita</item>
+ <item>spa</item>
+ <item>kor</item>
+ </string-array>
+
</resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java b/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
index cf17ea5..99bd4ad 100644
--- a/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
@@ -16,15 +16,20 @@
package com.android.settingslib.accessibility;
+import android.accessibilityservice.AccessibilityServiceInfo;
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.provider.Settings;
import android.text.TextUtils;
+import android.util.ArraySet;
+import android.view.accessibility.AccessibilityManager;
import java.util.Collections;
import java.util.HashSet;
+import java.util.List;
import java.util.Locale;
import java.util.Set;
@@ -45,7 +50,7 @@
return Collections.emptySet();
}
- final Set<ComponentName> enabledServices = new HashSet<ComponentName>();
+ final Set<ComponentName> enabledServices = new HashSet<>();
final TextUtils.SimpleStringSplitter colonSplitter = sStringColonSplitter;
colonSplitter.setString(enabledServicesSetting);
@@ -71,4 +76,79 @@
final Context langContext = context.createConfigurationContext(config);
return langContext.getText(resId);
}
+
+ public static void setAccessibilityServiceState(Context context, ComponentName toggledService,
+ boolean enabled) {
+ // Parse the enabled services.
+ Set<ComponentName> enabledServices = AccessibilityUtils.getEnabledServicesFromSettings(
+ context);
+
+ if (enabledServices.isEmpty()) {
+ enabledServices = new ArraySet<>(1);
+ }
+
+ // Determine enabled services and accessibility state.
+ boolean accessibilityEnabled = false;
+ if (enabled) {
+ enabledServices.add(toggledService);
+ // Enabling at least one service enables accessibility.
+ accessibilityEnabled = true;
+ } else {
+ enabledServices.remove(toggledService);
+ // Check how many enabled and installed services are present.
+ Set<ComponentName> installedServices = getInstalledServices(context);
+ for (ComponentName enabledService : enabledServices) {
+ if (installedServices.contains(enabledService)) {
+ // Disabling the last service disables accessibility.
+ accessibilityEnabled = true;
+ break;
+ }
+ }
+ }
+
+ // Update the enabled services setting.
+ StringBuilder enabledServicesBuilder = new StringBuilder();
+ // Keep the enabled services even if they are not installed since we
+ // have no way to know whether the application restore process has
+ // completed. In general the system should be responsible for the
+ // clean up not settings.
+ for (ComponentName enabledService : enabledServices) {
+ enabledServicesBuilder.append(enabledService.flattenToString());
+ enabledServicesBuilder.append(
+ AccessibilityUtils.ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR);
+ }
+ final int enabledServicesBuilderLength = enabledServicesBuilder.length();
+ if (enabledServicesBuilderLength > 0) {
+ enabledServicesBuilder.deleteCharAt(enabledServicesBuilderLength - 1);
+ }
+ Settings.Secure.putString(context.getContentResolver(),
+ Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+ enabledServicesBuilder.toString());
+
+ // Update accessibility enabled.
+ Settings.Secure.putInt(context.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_ENABLED, accessibilityEnabled ? 1 : 0);
+ }
+
+ private static Set<ComponentName> getInstalledServices(Context context) {
+ final Set<ComponentName> installedServices = new HashSet<>();
+ installedServices.clear();
+
+ final List<AccessibilityServiceInfo> installedServiceInfos =
+ AccessibilityManager.getInstance(context)
+ .getInstalledAccessibilityServiceList();
+ if (installedServiceInfos == null) {
+ return installedServices;
+ }
+
+ for (final AccessibilityServiceInfo info : installedServiceInfos) {
+ final ResolveInfo resolveInfo = info.getResolveInfo();
+ final ComponentName installedService = new ComponentName(
+ resolveInfo.serviceInfo.packageName,
+ resolveInfo.serviceInfo.name);
+ installedServices.add(installedService);
+ }
+ return installedServices;
+ }
+
}
diff --git a/packages/Shell/src/com/android/shell/BugreportReceiver.java b/packages/Shell/src/com/android/shell/BugreportReceiver.java
index 0d1ad19..e90a3b5 100644
--- a/packages/Shell/src/com/android/shell/BugreportReceiver.java
+++ b/packages/Shell/src/com/android/shell/BugreportReceiver.java
@@ -136,9 +136,10 @@
// EXTRA_TEXT should be an ArrayList, but some clients are expecting a single String.
// So, to avoid an exception on Intent.migrateExtraStreamToClipData(), we need to manually
// create the ClipData object with the attachments URIs.
- intent.putExtra(Intent.EXTRA_TEXT, SystemProperties.get("ro.build.description"));
- final ClipData clipData = new ClipData(
- null, new String[] { mimeType },
+ String messageBody = String.format("Build info: %s\nSerial number:%s",
+ SystemProperties.get("ro.build.description"), SystemProperties.get("ro.serialno"));
+ intent.putExtra(Intent.EXTRA_TEXT, messageBody);
+ final ClipData clipData = new ClipData(null, new String[] { mimeType },
new ClipData.Item(null, null, null, bugreportUri));
clipData.addItem(new ClipData.Item(null, null, null, screenshotUri));
intent.setClipData(clipData);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 794e900..049754e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -19,12 +19,14 @@
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
import android.animation.AnimatorListenerAdapter;
+import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Handler;
import android.os.Message;
+import android.provider.Settings;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
@@ -34,6 +36,7 @@
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
+
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.FontSizeUtils;
import com.android.systemui.R;
@@ -620,4 +623,9 @@
int getOffsetTop(TileRecord tile);
void updateResources();
}
+
+ public static boolean isTheNewQS(Context context) {
+ return Settings.Secure.getIntForUser(context.getContentResolver(), QS_THE_NEW_QS,
+ ActivityManager.getCurrentUser(), 0) != 0;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 61695b2..48b74a4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -28,6 +28,7 @@
import com.android.systemui.R;
import com.android.systemui.qs.QSDetailItems;
import com.android.systemui.qs.QSDetailItems.Item;
+import com.android.systemui.qs.QSPanel;
import com.android.systemui.qs.QSTile;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.CastController.CastDevice;
@@ -93,7 +94,7 @@
@Override
protected void handleUpdateState(BooleanState state, Object arg) {
state.visible = !mKeyguard.isSecure() || !mKeyguard.isShowing()
- || mKeyguard.canSkipBouncer();
+ || mKeyguard.canSkipBouncer() || QSPanel.isTheNewQS(mContext);
state.label = mContext.getString(R.string.quick_settings_cast_title);
state.value = false;
state.autoMirrorDrawable = false;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
index c6fc6ff..2f9a496 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
@@ -21,6 +21,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.Prefs;
import com.android.systemui.R;
+import com.android.systemui.qs.QSPanel;
import com.android.systemui.qs.QSTile;
import com.android.systemui.qs.SecureSetting;
import com.android.systemui.qs.UsageTracker;
@@ -110,7 +111,7 @@
protected void handleUpdateState(BooleanState state, Object arg) {
final int value = arg instanceof Integer ? (Integer) arg : mSetting.getValue();
final boolean enabled = value != 0;
- state.visible = enabled || mUsageTracker.isRecentlyUsed();
+ state.visible = enabled || mUsageTracker.isRecentlyUsed() || QSPanel.isTheNewQS(mContext);
state.value = enabled;
state.label = mContext.getString(R.string.quick_settings_inversion_label);
state.icon = enabled ? mEnable : mDisable;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index 7b83e6a..79084ae 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -23,6 +23,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.Prefs;
import com.android.systemui.R;
+import com.android.systemui.qs.QSPanel;
import com.android.systemui.qs.QSTile;
import com.android.systemui.qs.UsageTracker;
import com.android.systemui.statusbar.policy.HotspotController;
@@ -88,7 +89,8 @@
@Override
protected void handleUpdateState(BooleanState state, Object arg) {
- state.visible = mController.isHotspotSupported() && mUsageTracker.isRecentlyUsed();
+ state.visible = (mController.isHotspotSupported() && mUsageTracker.isRecentlyUsed())
+ || QSPanel.isTheNewQS(mContext);
state.label = mContext.getString(R.string.quick_settings_hotspot_label);
if (arg instanceof Boolean) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index e6fade4..0e2672c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -18,6 +18,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
+import com.android.systemui.qs.QSPanel;
import com.android.systemui.qs.QSTile;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
import com.android.systemui.statusbar.policy.LocationController;
@@ -73,7 +74,7 @@
// Work around for bug 15916487: don't show location tile on top of lock screen. After the
// bug is fixed, this should be reverted to only hiding it on secure lock screens:
// state.visible = !(mKeyguard.isSecure() && mKeyguard.isShowing());
- state.visible = !mKeyguard.isShowing();
+ state.visible = !mKeyguard.isShowing() || QSPanel.isTheNewQS(mContext);
state.value = locationEnabled;
if (locationEnabled) {
state.icon = mEnable;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 4d40cb7..a58bc58 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -21,9 +21,11 @@
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.res.Configuration;
+import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.util.Log;
import android.view.Display;
@@ -52,10 +54,21 @@
public final static int EVENT_BUS_PRIORITY = 1;
public final static int BIND_TO_SYSTEM_USER_RETRY_DELAY = 5000;
+ // Purely for experimentation
+ private final static String RECENTS_OVERRIDE_SYSPROP_KEY = "persist.recents_override_pkg";
+ private final static String ACTION_SHOW_RECENTS = "com.android.systemui.recents.ACTION_SHOW";
+ private final static String ACTION_HIDE_RECENTS = "com.android.systemui.recents.ACTION_HIDE";
+ private final static String ACTION_TOGGLE_RECENTS = "com.android.systemui.recents.ACTION_TOGGLE";
+
private static SystemServicesProxy sSystemServicesProxy;
private static RecentsTaskLoader sTaskLoader;
private static RecentsConfiguration sConfiguration;
+ // For experiments only, allows another package to handle recents if it is defined in the system
+ // properties. This is limited to show/toggle/hide, and does not tie into the ActivityManager,
+ // and does not reside in the home stack.
+ private String mOverrideRecentsPackageName;
+
private Handler mHandler;
private RecentsImpl mImpl;
@@ -142,6 +155,14 @@
mHandler = new Handler();
mImpl = new RecentsImpl(mContext);
+ // Check if there is a recents override package
+ if ("userdebug".equals(Build.TYPE) || "eng".equals(Build.TYPE)) {
+ String cnStr = SystemProperties.get(RECENTS_OVERRIDE_SYSPROP_KEY);
+ if (!cnStr.isEmpty()) {
+ mOverrideRecentsPackageName = cnStr;
+ }
+ }
+
// Register with the event bus
EventBus.getDefault().register(this, EVENT_BUS_PRIORITY);
EventBus.getDefault().register(sTaskLoader, EVENT_BUS_PRIORITY);
@@ -172,6 +193,10 @@
*/
@Override
public void showRecents(boolean triggeredFromAltTab, View statusBarView) {
+ if (proxyToOverridePackage(ACTION_SHOW_RECENTS)) {
+ return;
+ }
+
int currentUser = sSystemServicesProxy.getCurrentUser();
if (sSystemServicesProxy.isSystemUser(currentUser)) {
mImpl.showRecents(triggeredFromAltTab);
@@ -197,6 +222,10 @@
*/
@Override
public void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
+ if (proxyToOverridePackage(ACTION_HIDE_RECENTS)) {
+ return;
+ }
+
int currentUser = sSystemServicesProxy.getCurrentUser();
if (sSystemServicesProxy.isSystemUser(currentUser)) {
mImpl.hideRecents(triggeredFromAltTab, triggeredFromHomeKey);
@@ -222,6 +251,10 @@
*/
@Override
public void toggleRecents(Display display, int layoutDirection, View statusBarView) {
+ if (proxyToOverridePackage(ACTION_TOGGLE_RECENTS)) {
+ return;
+ }
+
int currentUser = sSystemServicesProxy.getCurrentUser();
if (sSystemServicesProxy.isSystemUser(currentUser)) {
mImpl.toggleRecents();
@@ -421,4 +454,19 @@
}
mOnConnectRunnables.clear();
}
+
+ /**
+ * Attempts to proxy the following action to the override recents package.
+ * @return whether the proxying was successful
+ */
+ private boolean proxyToOverridePackage(String action) {
+ if (mOverrideRecentsPackageName != null) {
+ Intent intent = new Intent(action);
+ intent.setPackage(mOverrideRecentsPackageName);
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ mContext.sendBroadcast(intent);
+ return true;
+ }
+ return false;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 4d575bc..b5a1a93 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -517,6 +517,15 @@
boolean hasRepKeyTimeElapsed = (SystemClock.elapsedRealtime() -
mLastTabKeyEventTime) > altTabKeyDelay;
if (event.getRepeatCount() <= 0 || hasRepKeyTimeElapsed) {
+ // As we iterate to the next/previous task, cancel any current/lagging window
+ // transition animations
+ RecentsConfiguration config = Recents.getConfiguration();
+ RecentsActivityLaunchState launchState = config.getLaunchState();
+ if (launchState.launchedToTaskId != -1) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ ssp.cancelWindowTransition(launchState.launchedToTaskId);
+ }
+
// Focus the next task in the stack
final boolean backward = event.isShiftPressed();
if (backward) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 243c0e1..0e11f02 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -16,7 +16,7 @@
package com.android.systemui.recents;
-import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import android.app.ActivityManager;
import android.app.ActivityOptions;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java
index 28299d3..d415845 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java
@@ -33,8 +33,8 @@
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.views.RecentsView;
-import static android.app.ActivityManager.DOCKED_STACK_ID;
-import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
/**
* A helper for the dialogs that show when task debugging is on.
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 141562c..bdd3440 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -57,6 +57,7 @@
import android.util.Pair;
import android.view.Display;
import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityManager;
import com.android.internal.app.AssistUtils;
import com.android.internal.os.BackgroundThread;
@@ -71,8 +72,10 @@
import java.util.List;
import java.util.Random;
-import static android.app.ActivityManager.DOCKED_STACK_ID;
-import static android.app.ActivityManager.INVALID_STACK_ID;
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.HOME_STACK_ID;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
/**
* Acts as a shim around the real system services that we need to access data from, and provides
@@ -310,14 +313,14 @@
* Returns whether the given stack id is the home stack id.
*/
public static boolean isHomeStack(int stackId) {
- return stackId == ActivityManager.HOME_STACK_ID;
+ return stackId == HOME_STACK_ID;
}
/**
* Returns whether the given stack id is the freeform workspace stack id.
*/
public static boolean isFreeformStack(int stackId) {
- return stackId == ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
+ return stackId == FREEFORM_WORKSPACE_STACK_ID;
}
/**
@@ -335,6 +338,19 @@
return stackInfo != null;
}
+ /**
+ * Cancels the current window transtion to/from Recents for the given task id.
+ */
+ public void cancelWindowTransition(int taskId) {
+ if (mWm == null) return;
+
+ try {
+ WindowManagerGlobal.getWindowManagerService().cancelTaskWindowTransition(taskId);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+
/** Returns the top task thumbnail for the given task id */
public Bitmap getTaskThumbnail(int taskId) {
if (mAm == null) return null;
@@ -714,7 +730,7 @@
try {
// Use the home stack bounds
- ActivityManager.StackInfo stackInfo = mIam.getStackInfo(ActivityManager.HOME_STACK_ID);
+ ActivityManager.StackInfo stackInfo = mIam.getStackInfo(HOME_STACK_ID);
if (stackInfo != null) {
windowRect.set(stackInfo.bounds);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
index 6c83b87..62493d6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -133,9 +133,11 @@
Task task = new Task(taskKey, (t.id != INVALID_TASK_ID), t.affiliatedTaskId,
t.affiliatedTaskColor, activityLabel, contentDescription, activityIcon,
activityColor, (i == (taskCount - 1)), config.lockToAppEnabled, icon,
- iconFilename);
+ iconFilename, t.bounds);
task.thumbnail = loader.getAndUpdateThumbnail(taskKey, ssp, false);
- if (DEBUG) Log.d(TAG, "\tthumbnail: " + taskKey + ", " + task.thumbnail);
+ if (DEBUG) {
+ Log.d(TAG, activityLabel + " bounds: " + t.bounds);
+ }
if (task.isFreeformTask()) {
freeformTasks.add(task);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
index 71e3957..bba453a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -560,6 +560,11 @@
ActivityInfo activityInfo = mActivityInfoCache.get(cn);
if (activityInfo == null) {
activityInfo = ssp.getActivityInfo(cn, taskKey.userId);
+ if (cn == null || activityInfo == null) {
+ Log.e(TAG, "Unexpected null component name or activity info: " + cn + ", " +
+ activityInfo);
+ return null;
+ }
mActivityInfoCache.put(cn, activityInfo);
}
return activityInfo;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index 60051b8..51a1ebc 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -20,6 +20,7 @@
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Color;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import com.android.systemui.recents.misc.Utilities;
@@ -105,6 +106,7 @@
public boolean lockToTaskEnabled;
public Bitmap icon;
public String iconFilename;
+ public Rect bounds;
private TaskCallbacks mCb;
@@ -115,7 +117,7 @@
public Task(TaskKey key, boolean isActive, int taskAffiliation, int taskAffiliationColor,
String activityTitle, String contentDescription, Drawable activityIcon,
int colorPrimary, boolean lockToThisTask, boolean lockToTaskEnabled, Bitmap icon,
- String iconFilename) {
+ String iconFilename, Rect bounds) {
boolean isInAffiliationGroup = (taskAffiliation != key.id);
boolean hasAffiliationGroupColor = isInAffiliationGroup && (taskAffiliationColor != 0);
this.key = key;
@@ -132,6 +134,7 @@
this.lockToTaskEnabled = lockToTaskEnabled;
this.icon = icon;
this.iconFilename = iconFilename;
+ this.bounds = bounds;
}
/** Copies the other task. */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskKeyLruCache.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskKeyLruCache.java
index 6d11f14..67a6a9f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskKeyLruCache.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskKeyLruCache.java
@@ -16,6 +16,7 @@
package com.android.systemui.recents.model;
+import android.util.Log;
import android.util.LruCache;
import android.util.SparseArray;
@@ -29,6 +30,8 @@
*/
public class TaskKeyLruCache<V> {
+ private static final String TAG = "TaskKeyLruCache";
+
private final SparseArray<Task.TaskKey> mKeys = new SparseArray<>();
private final LruCache<Integer, V> mCache;
@@ -71,6 +74,10 @@
/** Puts an entry in the cache for a specific key. */
final void put(Task.TaskKey key, V value) {
+ if (key == null || value == null) {
+ Log.e(TAG, "Unexpected null key or value: " + key + ", " + value);
+ return;
+ }
mKeys.put(key.id, key);
mCache.put(key.id, value);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 422e917..d72e50e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -44,6 +44,7 @@
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsActivity;
+import com.android.systemui.recents.RecentsActivityLaunchState;
import com.android.systemui.recents.RecentsAppWidgetHostView;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.events.EventBus;
@@ -60,7 +61,9 @@
import java.util.ArrayList;
import java.util.List;
-import static android.app.ActivityManager.INVALID_STACK_ID;
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
/**
* This view is the the top level layout that contains TaskStacks (which are laid out according
@@ -488,14 +491,14 @@
int destinationStack) {
final int targetStackId = destinationStack != INVALID_STACK_ID ?
destinationStack : clickedTask.getTask().key.stackId;
- if (targetStackId != ActivityManager.FREEFORM_WORKSPACE_STACK_ID
- && targetStackId != ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID) {
+ if (targetStackId != FREEFORM_WORKSPACE_STACK_ID
+ && targetStackId != FULLSCREEN_WORKSPACE_STACK_ID) {
return null;
}
// If this is a full screen stack, the transition will be towards the single, full screen
// task. We only need the transition spec for this task.
List<AppTransitionAnimationSpec> specs = new ArrayList<>();
- if (targetStackId == ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID) {
+ if (targetStackId == FULLSCREEN_WORKSPACE_STACK_ID) {
specs.add(createThumbnailHeaderAnimationSpec(
stackView, offsetX, offsetY, stackScroll, clickedTask,
clickedTask.getTask().key.id, ADD_HEADER_BITMAP));
@@ -587,6 +590,20 @@
return new AppTransitionAnimationSpec(taskId, b, rect);
}
+ /**
+ * Cancels any running window transitions for the launched task (the task animating into
+ * Recents).
+ */
+ private void cancelLaunchedTaskWindowTransition(final Task task) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ RecentsConfiguration config = Recents.getConfiguration();
+ RecentsActivityLaunchState launchState = config.getLaunchState();
+ if (launchState.launchedToTaskId != -1 &&
+ launchState.launchedToTaskId != task.key.id) {
+ ssp.cancelWindowTransition(launchState.launchedToTaskId);
+ }
+ }
+
/**** TaskStackView.TaskStackCallbacks Implementation ****/
@Override
@@ -617,34 +634,37 @@
// Compute the thumbnail to scale up from
final SystemServicesProxy ssp = Recents.getSystemServices();
+ boolean screenPinningRequested = false;
ActivityOptions opts = null;
ActivityOptions.OnAnimationStartedListener animStartedListener = null;
if (task.thumbnail != null && task.thumbnail.getWidth() > 0 &&
task.thumbnail.getHeight() > 0) {
- if (lockToTask) {
- animStartedListener = new ActivityOptions.OnAnimationStartedListener() {
- boolean mTriggered = false;
- @Override
- public void onAnimationStarted() {
- if (!mTriggered) {
- postDelayed(new Runnable() {
- @Override
- public void run() {
- EventBus.getDefault().send(new ScreenPinningRequestEvent(
- getContext(), ssp));
- }
- }, 350);
- mTriggered = true;
- }
+ animStartedListener = new ActivityOptions.OnAnimationStartedListener() {
+ @Override
+ public void onAnimationStarted() {
+ // If we are launching into another task, cancel the previous task's
+ // window transition
+ cancelLaunchedTaskWindowTransition(task);
+
+ if (lockToTask) {
+ // Request screen pinning after the animation runs
+ postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ EventBus.getDefault().send(new ScreenPinningRequestEvent(
+ getContext(), ssp));
+ }
+ }, 350);
}
- };
- }
+ }
+ };
postDrawHeaderThumbnailTransitionRunnable(stackView, tv, offsetX, offsetY, stackScroll,
animStartedListener, destinationStack);
opts = ActivityOptions.makeThumbnailAspectScaleUpAnimation(sourceView,
Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8).createAshmemBitmap(),
offsetX, offsetY, (int) transform.rect.width(), (int) transform.rect.height(),
sourceView.getHandler(), animStartedListener);
+ screenPinningRequested = true;
} else {
opts = ActivityOptions.makeBasic();
}
@@ -652,7 +672,7 @@
opts.setBounds(bounds.isEmpty() ? null : bounds);
}
final ActivityOptions launchOpts = opts;
- final boolean screenPinningRequested = (animStartedListener == null) && lockToTask;
+ final boolean finalScreenPinningRequested = screenPinningRequested;
final Runnable launchRunnable = new Runnable() {
@Override
public void run() {
@@ -660,9 +680,11 @@
// Bring an active task to the foreground
ssp.moveTaskToFront(task.key.id, launchOpts);
} else {
- if (ssp.startActivityFromRecents(getContext(), task.key.id,
- task.activityLabel, launchOpts)) {
- if (screenPinningRequested) {
+ if (ssp.startActivityFromRecents(getContext(), task.key.id, task.activityLabel,
+ launchOpts)) {
+ if (!finalScreenPinningRequested) {
+ // If we have not requested this already to be run after the window
+ // transition, then just run it now
EventBus.getDefault().send(new ScreenPinningRequestEvent(
getContext(), ssp));
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index b266eaa..e7f07f7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -58,7 +58,7 @@
import java.util.Iterator;
import java.util.List;
-import static android.app.ActivityManager.INVALID_STACK_ID;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
/* The visual representation of a task stack view */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
index 4ac2c31..a51f62a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.phone;
-import static android.app.ActivityManager.INVALID_STACK_ID;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import android.animation.LayoutTransition;
import android.annotation.Nullable;
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 ebe7785..fafedc3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -128,7 +128,6 @@
};
protected void onExpandingFinished() {
- endClosing();
mBar.onExpandingFinished();
}
@@ -143,6 +142,7 @@
}
protected final void notifyExpandingFinished() {
+ endClosing();
if (mExpanding) {
mExpanding = false;
onExpandingFinished();
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 749a080..535a8ef 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -91,7 +91,6 @@
import com.android.internal.R;
import com.android.internal.content.PackageMonitor;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.widget.LockPatternUtils;
import com.android.server.LocalServices;
import org.xmlpull.v1.XmlPullParserException;
@@ -180,6 +179,8 @@
private final MainHandler mMainHandler;
+ private MagnificationController mMagnificationController;
+
private InteractionBridge mInteractionBridge;
private AlertDialog mEnableTouchExplorationDialog;
@@ -1936,6 +1937,13 @@
}
}
+ MagnificationController getMagnificationController() {
+ if (mMagnificationController == null) {
+ mMagnificationController = new MagnificationController(mContext, this);
+ }
+ return mMagnificationController;
+ }
+
/**
* This class represents an accessibility service. It stores all per service
* data required for the service management, provides API for starting/stopping the
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
new file mode 100644
index 0000000..b4411cf
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
@@ -0,0 +1,367 @@
+/*
+ * Copyright (C) 2015 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.accessibility;
+
+import com.android.internal.R;
+import com.android.server.LocalServices;
+
+import android.animation.ObjectAnimator;
+import android.animation.TypeEvaluator;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.util.Property;
+import android.util.Slog;
+import android.view.MagnificationSpec;
+import android.view.WindowManagerInternal;
+import android.view.animation.DecelerateInterpolator;
+
+/**
+ * This class is used to control and query the state of display magnification
+ * from the accessibility manager and related classes. It is responsible for
+ * holding the current state of magnification and animation, and it handles
+ * communication between the accessibility manager and window manager.
+ */
+class MagnificationController {
+ private static final String LOG_TAG = ScreenMagnifier.class.getSimpleName();
+
+ private static final boolean DEBUG_SET_MAGNIFICATION_SPEC = false;
+ private static final boolean DEBUG_MAGNIFICATION_CONTROLLER = false;
+
+ private static final String PROPERTY_NAME_MAGNIFICATION_SPEC = "magnificationSpec";
+
+ private final MagnificationSpec mSentMagnificationSpec = MagnificationSpec.obtain();
+ private final MagnificationSpec mCurrentMagnificationSpec = MagnificationSpec.obtain();
+
+ private final Region mMagnifiedBounds = new Region();
+ private final Rect mTempRect = new Rect();
+
+ private final AccessibilityManagerService mAms;
+ private final WindowManagerInternal mWindowManager;
+ private final ValueAnimator mTransformationAnimator;
+
+ public MagnificationController(Context context, AccessibilityManagerService ams) {
+ mAms = ams;
+ mWindowManager = LocalServices.getService(WindowManagerInternal.class);
+
+ final Property<MagnificationController, MagnificationSpec> property =
+ Property.of(MagnificationController.class, MagnificationSpec.class,
+ PROPERTY_NAME_MAGNIFICATION_SPEC);
+ final MagnificationSpecEvaluator evaluator = new MagnificationSpecEvaluator();
+ final long animationDuration = context.getResources().getInteger(
+ R.integer.config_longAnimTime);
+ mTransformationAnimator = ObjectAnimator.ofObject(this, property, evaluator,
+ mSentMagnificationSpec, mCurrentMagnificationSpec);
+ mTransformationAnimator.setDuration(animationDuration);
+ mTransformationAnimator.setInterpolator(new DecelerateInterpolator(2.5f));
+ }
+
+ /**
+ * @return {@code true} if magnification is active, e.g. the scale
+ * is > 1, {@code false} otherwise
+ */
+ public boolean isMagnifying() {
+ return mCurrentMagnificationSpec.scale > 1.0f;
+ }
+
+ /**
+ * Sets the magnified region.
+ *
+ * @param region the region to set
+ * @param updateSpec {@code true} to update the scale and center based on
+ * the region bounds, {@code false} to leave them as-is
+ */
+ public void setMagnifiedRegion(Region region, boolean updateSpec) {
+ mMagnifiedBounds.set(region);
+
+ if (updateSpec) {
+ final Rect magnifiedFrame = mTempRect;
+ region.getBounds(magnifiedFrame);
+ final float scale = mSentMagnificationSpec.scale;
+ final float offsetX = mSentMagnificationSpec.offsetX;
+ final float offsetY = mSentMagnificationSpec.offsetY;
+ final float centerX = (-offsetX + magnifiedFrame.width() / 2) / scale;
+ final float centerY = (-offsetY + magnifiedFrame.height() / 2) / scale;
+ setScaleAndMagnifiedRegionCenter(scale, centerX, centerY, false);
+ } else {
+ mAms.onMagnificationStateChanged();
+ }
+ }
+
+ /**
+ * Returns whether the magnified region contains the specified
+ * screen-relative coordinates.
+ *
+ * @param x the screen-relative X coordinate to check
+ * @param y the screen-relative Y coordinate to check
+ * @return {@code true} if the coordinate is contained within the
+ * magnified region, or {@code false} otherwise
+ */
+ public boolean magnifiedRegionContains(float x, float y) {
+ return mMagnifiedBounds.contains((int) x, (int) y);
+ }
+
+ /**
+ * Populates the specified rect with the bounds of the magnified
+ * region.
+ *
+ * @param outBounds rect to populate with the bounds of the magnified
+ * region
+ */
+ public void getMagnifiedBounds(Rect outBounds) {
+ mMagnifiedBounds.getBounds(outBounds);
+ }
+
+ /**
+ * Returns the magnification scale. If an animation is in progress,
+ * this reflects the end state of the animation.
+ *
+ * @return the scale
+ */
+ public float getScale() {
+ return mCurrentMagnificationSpec.scale;
+ }
+
+ /**
+ * Returns the X offset of the magnification viewport. If an animation
+ * is in progress, this reflects the end state of the animation.
+ *
+ * @return the X offset
+ */
+ public float getOffsetX() {
+ return mCurrentMagnificationSpec.offsetX;
+ }
+
+ /**
+ * Returns the Y offset of the magnification viewport. If an animation
+ * is in progress, this reflects the end state of the animation.
+ *
+ * @return the Y offset
+ */
+ public float getOffsetY() {
+ return mCurrentMagnificationSpec.offsetY;
+ }
+
+ /**
+ * Returns the scale currently used by the window manager. If an
+ * animation is in progress, this reflects the current state of the
+ * animation.
+ *
+ * @return the scale currently used by the window manager
+ */
+ public float getSentScale() {
+ return mSentMagnificationSpec.scale;
+ }
+
+ /**
+ * Returns the X offset currently used by the window manager. If an
+ * animation is in progress, this reflects the current state of the
+ * animation.
+ *
+ * @return the X offset currently used by the window manager
+ */
+ public float getSentOffsetX() {
+ return mSentMagnificationSpec.offsetX;
+ }
+
+ /**
+ * Returns the Y offset currently used by the window manager. If an
+ * animation is in progress, this reflects the current state of the
+ * animation.
+ *
+ * @return the Y offset currently used by the window manager
+ */
+ public float getSentOffsetY() {
+ return mSentMagnificationSpec.offsetY;
+ }
+
+ /**
+ * Resets the magnification scale and center, optionally animating the
+ * transition.
+ *
+ * @param animate {@code true} to animate the transition, {@code false}
+ * to transition immediately
+ */
+ public void reset(boolean animate) {
+ if (mTransformationAnimator.isRunning()) {
+ mTransformationAnimator.cancel();
+ }
+ mCurrentMagnificationSpec.clear();
+ if (animate) {
+ animateMagnificationSpec(mSentMagnificationSpec,
+ mCurrentMagnificationSpec);
+ } else {
+ setMagnificationSpec(mCurrentMagnificationSpec);
+ }
+ final Rect bounds = mTempRect;
+ bounds.setEmpty();
+ mAms.onMagnificationStateChanged();
+ }
+
+ /**
+ * Scales the magnified region around the specified pivot point,
+ * optionally animating the transition. If animation is disabled, the
+ * transition is immediate.
+ *
+ * @param scale the target scale, must be >= 1
+ * @param animate {@code true} to animate the transition, {@code false}
+ * to transition immediately
+ */
+ public void setScale(float scale, float pivotX, float pivotY, boolean animate) {
+ final Rect magnifiedFrame = mTempRect;
+ mMagnifiedBounds.getBounds(magnifiedFrame);
+ final MagnificationSpec spec = mCurrentMagnificationSpec;
+ final float oldScale = spec.scale;
+ final float oldCenterX = (-spec.offsetX + magnifiedFrame.width() / 2) / oldScale;
+ final float oldCenterY = (-spec.offsetY + magnifiedFrame.height() / 2) / oldScale;
+ final float normPivotX = (-spec.offsetX + pivotX) / oldScale;
+ final float normPivotY = (-spec.offsetY + pivotY) / oldScale;
+ final float offsetX = (oldCenterX - normPivotX) * (oldScale / scale);
+ final float offsetY = (oldCenterY - normPivotY) * (oldScale / scale);
+ final float centerX = normPivotX + offsetX;
+ final float centerY = normPivotY + offsetY;
+ setScaleAndMagnifiedRegionCenter(scale, centerX, centerY, animate);
+ }
+
+ /**
+ * Sets the center of the magnified region, optionally animating the
+ * transition. If animation is disabled, the transition is immediate.
+ *
+ * @param centerX the screen-relative X coordinate around which to
+ * center
+ * @param centerY the screen-relative Y coordinate around which to
+ * center
+ * @param animate {@code true} to animate the transition, {@code false}
+ * to transition immediately
+ */
+ public void setMagnifiedRegionCenter(float centerX, float centerY, boolean animate) {
+ setScaleAndMagnifiedRegionCenter(mCurrentMagnificationSpec.scale, centerX, centerY,
+ animate);
+ }
+
+ /**
+ * Sets the scale and center of the magnified region, optionally
+ * animating the transition. If animation is disabled, the transition
+ * is immediate.
+ *
+ * @param scale the target scale, must be >= 1
+ * @param centerX the screen-relative X coordinate around which to
+ * center and scale
+ * @param centerY the screen-relative Y coordinate around which to
+ * center and scale
+ * @param animate {@code true} to animate the transition, {@code false}
+ * to transition immediately
+ */
+ public void setScaleAndMagnifiedRegionCenter(float scale, float centerX, float centerY,
+ boolean animate) {
+ if (Float.compare(mCurrentMagnificationSpec.scale, scale) == 0
+ && Float.compare(mCurrentMagnificationSpec.offsetX, centerX) == 0
+ && Float.compare(mCurrentMagnificationSpec.offsetY, centerY) == 0) {
+ return;
+ }
+ if (mTransformationAnimator.isRunning()) {
+ mTransformationAnimator.cancel();
+ }
+ if (DEBUG_MAGNIFICATION_CONTROLLER) {
+ Slog.i(LOG_TAG, "scale: " + scale + " offsetX: " + centerX + " offsetY: " + centerY);
+ }
+ updateMagnificationSpec(scale, centerX, centerY);
+ if (animate) {
+ animateMagnificationSpec(mSentMagnificationSpec,
+ mCurrentMagnificationSpec);
+ } else {
+ setMagnificationSpec(mCurrentMagnificationSpec);
+ }
+ mAms.onMagnificationStateChanged();
+ }
+
+ /**
+ * Offsets the center of the magnified region.
+ *
+ * @param offsetX the amount in pixels to offset the X center
+ * @param offsetY the amount in pixels to offset the Y center
+ */
+ public void offsetMagnifiedRegionCenter(float offsetX, float offsetY) {
+ final float nonNormOffsetX = mCurrentMagnificationSpec.offsetX - offsetX;
+ mCurrentMagnificationSpec.offsetX = Math.min(Math.max(nonNormOffsetX,
+ getMinOffsetX()), 0);
+ final float nonNormOffsetY = mCurrentMagnificationSpec.offsetY - offsetY;
+ mCurrentMagnificationSpec.offsetY = Math.min(Math.max(nonNormOffsetY,
+ getMinOffsetY()), 0);
+ setMagnificationSpec(mCurrentMagnificationSpec);
+ }
+
+ private void updateMagnificationSpec(float scale, float magnifiedCenterX,
+ float magnifiedCenterY) {
+ final Rect magnifiedFrame = mTempRect;
+ mMagnifiedBounds.getBounds(magnifiedFrame);
+ mCurrentMagnificationSpec.scale = scale;
+ final int viewportWidth = magnifiedFrame.width();
+ final float nonNormOffsetX = viewportWidth / 2 - magnifiedCenterX * scale;
+ mCurrentMagnificationSpec.offsetX = Math.min(Math.max(nonNormOffsetX,
+ getMinOffsetX()), 0);
+ final int viewportHeight = magnifiedFrame.height();
+ final float nonNormOffsetY = viewportHeight / 2 - magnifiedCenterY * scale;
+ mCurrentMagnificationSpec.offsetY = Math.min(Math.max(nonNormOffsetY,
+ getMinOffsetY()), 0);
+ }
+
+ private float getMinOffsetX() {
+ final Rect magnifiedFrame = mTempRect;
+ mMagnifiedBounds.getBounds(magnifiedFrame);
+ final float viewportWidth = magnifiedFrame.width();
+ return viewportWidth - viewportWidth * mCurrentMagnificationSpec.scale;
+ }
+
+ private float getMinOffsetY() {
+ final Rect magnifiedFrame = mTempRect;
+ mMagnifiedBounds.getBounds(magnifiedFrame);
+ final float viewportHeight = magnifiedFrame.height();
+ return viewportHeight - viewportHeight * mCurrentMagnificationSpec.scale;
+ }
+
+ private void animateMagnificationSpec(MagnificationSpec fromSpec,
+ MagnificationSpec toSpec) {
+ mTransformationAnimator.setObjectValues(fromSpec, toSpec);
+ mTransformationAnimator.start();
+ }
+
+ private void setMagnificationSpec(MagnificationSpec spec) {
+ if (DEBUG_SET_MAGNIFICATION_SPEC) {
+ Slog.i(LOG_TAG, "Sending: " + spec);
+ }
+ mSentMagnificationSpec.scale = spec.scale;
+ mSentMagnificationSpec.offsetX = spec.offsetX;
+ mSentMagnificationSpec.offsetY = spec.offsetY;
+ mWindowManager.setMagnificationSpec(MagnificationSpec.obtain(spec));
+ }
+
+ private static class MagnificationSpecEvaluator implements TypeEvaluator<MagnificationSpec> {
+ private final MagnificationSpec mTempTransformationSpec = MagnificationSpec.obtain();
+
+ @Override
+ public MagnificationSpec evaluate(float fraction, MagnificationSpec fromSpec,
+ MagnificationSpec toSpec) {
+ final MagnificationSpec result = mTempTransformationSpec;
+ result.scale = fromSpec.scale + (toSpec.scale - fromSpec.scale) * fraction;
+ result.offsetX = fromSpec.offsetX + (toSpec.offsetX - fromSpec.offsetX) * fraction;
+ result.offsetY = fromSpec.offsetY + (toSpec.offsetY - fromSpec.offsetY) * fraction;
+ return result;
+ }
+ }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java b/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java
index 8845bc0..8feb167 100644
--- a/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java
+++ b/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java
@@ -16,9 +16,6 @@
package com.android.server.accessibility;
-import android.animation.ObjectAnimator;
-import android.animation.TypeEvaluator;
-import android.animation.ValueAnimator;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -31,14 +28,12 @@
import android.os.Message;
import android.provider.Settings;
import android.text.TextUtils;
-import android.util.Property;
import android.util.Slog;
import android.util.TypedValue;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.InputDevice;
import android.view.KeyEvent;
-import android.view.MagnificationSpec;
import android.view.MotionEvent;
import android.view.MotionEvent.PointerCoords;
import android.view.MotionEvent.PointerProperties;
@@ -48,7 +43,6 @@
import android.view.ViewConfiguration;
import android.view.WindowManagerInternal;
import android.view.accessibility.AccessibilityEvent;
-import android.view.animation.DecelerateInterpolator;
import com.android.internal.os.SomeArgs;
import com.android.server.LocalServices;
@@ -101,10 +95,8 @@
private static final boolean DEBUG_STATE_TRANSITIONS = false;
private static final boolean DEBUG_DETECTING = false;
- private static final boolean DEBUG_SET_MAGNIFICATION_SPEC = false;
private static final boolean DEBUG_PANNING = false;
private static final boolean DEBUG_SCALING = false;
- private static final boolean DEBUG_MAGNIFICATION_CONTROLLER = false;
private static final int STATE_DELEGATING = 1;
private static final int STATE_DETECTING = 2;
@@ -134,8 +126,6 @@
private final MagnifiedContentInteractonStateHandler mMagnifiedContentInteractonStateHandler;
private final StateViewportDraggingHandler mStateViewportDraggingHandler;
- private final AccessibilityManagerService mAms;
-
private final int mUserId;
private final int mTapTimeSlop = ViewConfiguration.getJumpTapTimeout();
@@ -143,10 +133,6 @@
private final int mTapDistanceSlop;
private final int mMultiTapDistanceSlop;
- private final long mLongAnimationDuration;
-
- private final Region mMagnifiedBounds = new Region();
-
private EventStreamTransformation mNext;
private int mCurrentState;
@@ -194,13 +180,10 @@
mContext = context;
mUserId = userId;
mWindowManager = LocalServices.getService(WindowManagerInternal.class);
- mAms = service;
mMultiTapTimeSlop = ViewConfiguration.getDoubleTapTimeout()
+ mContext.getResources().getInteger(
com.android.internal.R.integer.config_screen_magnification_multi_tap_adjustment);
- mLongAnimationDuration = context.getResources().getInteger(
- com.android.internal.R.integer.config_longAnimTime);
mTapDistanceSlop = ViewConfiguration.get(context).getScaledTouchSlop();
mMultiTapDistanceSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop();
@@ -209,7 +192,7 @@
mMagnifiedContentInteractonStateHandler = new MagnifiedContentInteractonStateHandler(
context);
- mMagnificationController = new MagnificationController(mLongAnimationDuration);
+ mMagnificationController = service.getMagnificationController();
mScreenStateObserver = new ScreenStateObserver(context, mMagnificationController);
mWindowManager.setMagnificationCallbacks(this);
@@ -230,19 +213,9 @@
// If there was a rotation we have to update the center of the magnified
// region since the old offset X/Y may be out of its acceptable range for
// the new display width and height.
- if (mUpdateMagnificationSpecOnNextBoundsChange) {
- mUpdateMagnificationSpecOnNextBoundsChange = false;
- MagnificationSpec spec = mMagnificationController.getMagnificationSpec();
- Rect magnifiedFrame = mTempRect;
- mMagnifiedBounds.getBounds(magnifiedFrame);
- final float scale = spec.scale;
- final float centerX = (-spec.offsetX + magnifiedFrame.width() / 2) / scale;
- final float centerY = (-spec.offsetY + magnifiedFrame.height() / 2) / scale;
- mMagnificationController.setScaleAndMagnifiedRegionCenter(scale, centerX,
- centerY, false);
- }
- mMagnifiedBounds.set(bounds);
- mAms.onMagnificationStateChanged();
+ mMagnificationController.setMagnifiedRegion(
+ bounds, mUpdateMagnificationSpecOnNextBoundsChange);
+ mUpdateMagnificationSpecOnNextBoundsChange = false;
}
@Override
@@ -257,7 +230,7 @@
private void handleOnRectangleOnScreenRequested(int left, int top, int right, int bottom) {
Rect magnifiedFrame = mTempRect;
- mMagnifiedBounds.getBounds(magnifiedFrame);
+ mMagnificationController.getMagnifiedBounds(magnifiedFrame);
if (!magnifiedFrame.intersects(left, top, right, bottom)) {
return;
}
@@ -314,10 +287,12 @@
}
private void getMagnifiedFrameInContentCoords(Rect rect) {
- MagnificationSpec spec = mMagnificationController.getMagnificationSpec();
- mMagnifiedBounds.getBounds(rect);
- rect.offset((int) -spec.offsetX, (int) -spec.offsetY);
- rect.scale(1.0f / spec.scale);
+ final float scale = mMagnificationController.getSentScale();
+ final float offsetX = mMagnificationController.getSentOffsetX();
+ final float offsetY = mMagnificationController.getSentOffsetY();
+ mMagnificationController.getMagnifiedBounds(rect);
+ rect.offset((int) -offsetX, (int) -offsetY);
+ rect.scale(1.0f / scale);
}
private void resetMagnificationIfNeeded() {
@@ -421,7 +396,7 @@
final float eventX = event.getX();
final float eventY = event.getY();
if (mMagnificationController.isMagnifying()
- && mMagnifiedBounds.contains((int) eventX, (int) eventY)) {
+ && mMagnificationController.magnifiedRegionContains(eventX, eventY)) {
final float scale = mMagnificationController.getScale();
final float scaledOffsetX = mMagnificationController.getOffsetX();
final float scaledOffsetY = mMagnificationController.getOffsetY();
@@ -623,7 +598,7 @@
}
final float eventX = event.getX();
final float eventY = event.getY();
- if (mMagnifiedBounds.contains((int) eventX, (int) eventY)) {
+ if (mMagnificationController.magnifiedRegionContains(eventX, eventY)) {
if (mLastMoveOutsideMagnifiedRegion) {
mLastMoveOutsideMagnifiedRegion = false;
mMagnificationController.setMagnifiedRegionCenter(eventX,
@@ -696,8 +671,8 @@
switch (action) {
case MotionEvent.ACTION_DOWN: {
mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE);
- if (!mMagnifiedBounds.contains((int) event.getX(),
- (int) event.getY())) {
+ if (!mMagnificationController.magnifiedRegionContains(
+ event.getX(), event.getY())) {
transitionToDelegatingStateAndClear();
return;
}
@@ -738,7 +713,8 @@
return;
}
mHandler.removeMessages(MESSAGE_ON_ACTION_TAP_AND_HOLD);
- if (!mMagnifiedBounds.contains((int) event.getX(), (int) event.getY())) {
+ if (!mMagnificationController.magnifiedRegionContains(
+ event.getX(), event.getY())) {
transitionToDelegatingStateAndClear();
return;
}
@@ -947,184 +923,6 @@
}
}
- private final class MagnificationController {
-
- private static final String PROPERTY_NAME_MAGNIFICATION_SPEC =
- "magnificationSpec";
-
- private final MagnificationSpec mSentMagnificationSpec = MagnificationSpec.obtain();
-
- private final MagnificationSpec mCurrentMagnificationSpec = MagnificationSpec.obtain();
-
- private final Rect mTempRect = new Rect();
-
- private final ValueAnimator mTransformationAnimator;
-
- public MagnificationController(long animationDuration) {
- Property<MagnificationController, MagnificationSpec> property =
- Property.of(MagnificationController.class, MagnificationSpec.class,
- PROPERTY_NAME_MAGNIFICATION_SPEC);
- TypeEvaluator<MagnificationSpec> evaluator = new TypeEvaluator<MagnificationSpec>() {
- private final MagnificationSpec mTempTransformationSpec =
- MagnificationSpec.obtain();
- @Override
- public MagnificationSpec evaluate(float fraction, MagnificationSpec fromSpec,
- MagnificationSpec toSpec) {
- MagnificationSpec result = mTempTransformationSpec;
- result.scale = fromSpec.scale
- + (toSpec.scale - fromSpec.scale) * fraction;
- result.offsetX = fromSpec.offsetX + (toSpec.offsetX - fromSpec.offsetX)
- * fraction;
- result.offsetY = fromSpec.offsetY + (toSpec.offsetY - fromSpec.offsetY)
- * fraction;
- return result;
- }
- };
- mTransformationAnimator = ObjectAnimator.ofObject(this, property,
- evaluator, mSentMagnificationSpec, mCurrentMagnificationSpec);
- mTransformationAnimator.setDuration((long) (animationDuration));
- mTransformationAnimator.setInterpolator(new DecelerateInterpolator(2.5f));
- }
-
- public boolean isMagnifying() {
- return mCurrentMagnificationSpec.scale > 1.0f;
- }
-
- public void reset(boolean animate) {
- if (mTransformationAnimator.isRunning()) {
- mTransformationAnimator.cancel();
- }
- mCurrentMagnificationSpec.clear();
- if (animate) {
- animateMangificationSpec(mSentMagnificationSpec,
- mCurrentMagnificationSpec);
- } else {
- setMagnificationSpec(mCurrentMagnificationSpec);
- }
- Rect bounds = mTempRect;
- bounds.setEmpty();
- mAms.onMagnificationStateChanged();
- }
-
- public float getScale() {
- return mCurrentMagnificationSpec.scale;
- }
-
- public float getOffsetX() {
- return mCurrentMagnificationSpec.offsetX;
- }
-
- public float getOffsetY() {
- return mCurrentMagnificationSpec.offsetY;
- }
-
- public void setScale(float scale, float pivotX, float pivotY, boolean animate) {
- Rect magnifiedFrame = mTempRect;
- mMagnifiedBounds.getBounds(magnifiedFrame);
- MagnificationSpec spec = mCurrentMagnificationSpec;
- final float oldScale = spec.scale;
- final float oldCenterX = (-spec.offsetX + magnifiedFrame.width() / 2) / oldScale;
- final float oldCenterY = (-spec.offsetY + magnifiedFrame.height() / 2) / oldScale;
- final float normPivotX = (-spec.offsetX + pivotX) / oldScale;
- final float normPivotY = (-spec.offsetY + pivotY) / oldScale;
- final float offsetX = (oldCenterX - normPivotX) * (oldScale / scale);
- final float offsetY = (oldCenterY - normPivotY) * (oldScale / scale);
- final float centerX = normPivotX + offsetX;
- final float centerY = normPivotY + offsetY;
- setScaleAndMagnifiedRegionCenter(scale, centerX, centerY, animate);
- }
-
- public void setMagnifiedRegionCenter(float centerX, float centerY, boolean animate) {
- setScaleAndMagnifiedRegionCenter(mCurrentMagnificationSpec.scale, centerX, centerY,
- animate);
- }
-
- public void offsetMagnifiedRegionCenter(float offsetX, float offsetY) {
- final float nonNormOffsetX = mCurrentMagnificationSpec.offsetX - offsetX;
- mCurrentMagnificationSpec.offsetX = Math.min(Math.max(nonNormOffsetX,
- getMinOffsetX()), 0);
- final float nonNormOffsetY = mCurrentMagnificationSpec.offsetY - offsetY;
- mCurrentMagnificationSpec.offsetY = Math.min(Math.max(nonNormOffsetY,
- getMinOffsetY()), 0);
- setMagnificationSpec(mCurrentMagnificationSpec);
- }
-
- public void setScaleAndMagnifiedRegionCenter(float scale, float centerX, float centerY,
- boolean animate) {
- if (Float.compare(mCurrentMagnificationSpec.scale, scale) == 0
- && Float.compare(mCurrentMagnificationSpec.offsetX,
- centerX) == 0
- && Float.compare(mCurrentMagnificationSpec.offsetY,
- centerY) == 0) {
- return;
- }
- if (mTransformationAnimator.isRunning()) {
- mTransformationAnimator.cancel();
- }
- if (DEBUG_MAGNIFICATION_CONTROLLER) {
- Slog.i(LOG_TAG, "scale: " + scale + " offsetX: " + centerX
- + " offsetY: " + centerY);
- }
- updateMagnificationSpec(scale, centerX, centerY);
- if (animate) {
- animateMangificationSpec(mSentMagnificationSpec,
- mCurrentMagnificationSpec);
- } else {
- setMagnificationSpec(mCurrentMagnificationSpec);
- }
- mAms.onMagnificationStateChanged();
- }
-
- public void updateMagnificationSpec(float scale, float magnifiedCenterX,
- float magnifiedCenterY) {
- Rect magnifiedFrame = mTempRect;
- mMagnifiedBounds.getBounds(magnifiedFrame);
- mCurrentMagnificationSpec.scale = scale;
- final int viewportWidth = magnifiedFrame.width();
- final float nonNormOffsetX = viewportWidth / 2 - magnifiedCenterX * scale;
- mCurrentMagnificationSpec.offsetX = Math.min(Math.max(nonNormOffsetX,
- getMinOffsetX()), 0);
- final int viewportHeight = magnifiedFrame.height();
- final float nonNormOffsetY = viewportHeight / 2 - magnifiedCenterY * scale;
- mCurrentMagnificationSpec.offsetY = Math.min(Math.max(nonNormOffsetY,
- getMinOffsetY()), 0);
- }
-
- private float getMinOffsetX() {
- Rect magnifiedFrame = mTempRect;
- mMagnifiedBounds.getBounds(magnifiedFrame);
- final float viewportWidth = magnifiedFrame.width();
- return viewportWidth - viewportWidth * mCurrentMagnificationSpec.scale;
- }
-
- private float getMinOffsetY() {
- Rect magnifiedFrame = mTempRect;
- mMagnifiedBounds.getBounds(magnifiedFrame);
- final float viewportHeight = magnifiedFrame.height();
- return viewportHeight - viewportHeight * mCurrentMagnificationSpec.scale;
- }
-
- private void animateMangificationSpec(MagnificationSpec fromSpec,
- MagnificationSpec toSpec) {
- mTransformationAnimator.setObjectValues(fromSpec, toSpec);
- mTransformationAnimator.start();
- }
-
- public MagnificationSpec getMagnificationSpec() {
- return mSentMagnificationSpec;
- }
-
- public void setMagnificationSpec(MagnificationSpec spec) {
- if (DEBUG_SET_MAGNIFICATION_SPEC) {
- Slog.i(LOG_TAG, "Sending: " + spec);
- }
- mSentMagnificationSpec.scale = spec.scale;
- mSentMagnificationSpec.offsetX = spec.offsetX;
- mSentMagnificationSpec.offsetY = spec.offsetY;
- mWindowManager.setMagnificationSpec(MagnificationSpec.obtain(spec));
- }
- }
-
private final class ScreenStateObserver extends BroadcastReceiver {
private static final int MESSAGE_ON_SCREEN_STATE_CHANGE = 1;
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 61fe62f..48e96aa 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -123,6 +123,7 @@
private int mLastBatteryTemperature;
private boolean mLastBatteryLevelCritical;
private int mLastMaxChargingCurrent;
+ private int mLastMaxChargingVoltage;
private int mInvalidCharger;
private int mLastInvalidCharger;
@@ -339,6 +340,7 @@
+ ", chargerUsbOnline=" + mBatteryProps.chargerUsbOnline
+ ", chargerWirelessOnline=" + mBatteryProps.chargerWirelessOnline
+ ", maxChargingCurrent" + mBatteryProps.maxChargingCurrent
+ + ", maxChargingVoltage" + mBatteryProps.maxChargingVoltage
+ ", batteryStatus=" + mBatteryProps.batteryStatus
+ ", batteryHealth=" + mBatteryProps.batteryHealth
+ ", batteryPresent=" + mBatteryProps.batteryPresent
@@ -370,6 +372,7 @@
mBatteryProps.batteryVoltage != mLastBatteryVoltage ||
mBatteryProps.batteryTemperature != mLastBatteryTemperature ||
mBatteryProps.maxChargingCurrent != mLastMaxChargingCurrent ||
+ mBatteryProps.maxChargingVoltage != mLastMaxChargingVoltage ||
mInvalidCharger != mLastInvalidCharger)) {
if (mPlugType != mLastPlugType) {
@@ -497,6 +500,7 @@
mLastBatteryVoltage = mBatteryProps.batteryVoltage;
mLastBatteryTemperature = mBatteryProps.batteryTemperature;
mLastMaxChargingCurrent = mBatteryProps.maxChargingCurrent;
+ mLastMaxChargingVoltage = mBatteryProps.maxChargingVoltage;
mLastBatteryLevelCritical = mBatteryLevelCritical;
mLastInvalidCharger = mInvalidCharger;
}
@@ -522,7 +526,7 @@
intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mBatteryProps.batteryTechnology);
intent.putExtra(BatteryManager.EXTRA_INVALID_CHARGER, mInvalidCharger);
intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_CURRENT, mBatteryProps.maxChargingCurrent);
-
+ intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE, mBatteryProps.maxChargingVoltage);
if (DEBUG) {
Slog.d(TAG, "Sending ACTION_BATTERY_CHANGED. level:" + mBatteryProps.batteryLevel +
", scale:" + BATTERY_SCALE + ", status:" + mBatteryProps.batteryStatus +
@@ -535,7 +539,8 @@
", USB powered:" + mBatteryProps.chargerUsbOnline +
", Wireless powered:" + mBatteryProps.chargerWirelessOnline +
", icon:" + icon + ", invalid charger:" + mInvalidCharger +
- ", maxChargingCurrent:" + mBatteryProps.maxChargingCurrent);
+ ", maxChargingCurrent:" + mBatteryProps.maxChargingCurrent +
+ ", maxChargingVoltage:" + mBatteryProps.maxChargingVoltage);
}
mHandler.post(new Runnable() {
@@ -766,6 +771,7 @@
pw.println(" USB powered: " + mBatteryProps.chargerUsbOnline);
pw.println(" Wireless powered: " + mBatteryProps.chargerWirelessOnline);
pw.println(" Max charging current: " + mBatteryProps.maxChargingCurrent);
+ pw.println(" Max charging voltage: " + mBatteryProps.maxChargingVoltage);
pw.println(" status: " + mBatteryProps.batteryStatus);
pw.println(" health: " + mBatteryProps.batteryHealth);
pw.println(" present: " + mBatteryProps.batteryPresent);
diff --git a/services/core/java/com/android/server/NetworkTimeUpdateService.java b/services/core/java/com/android/server/NetworkTimeUpdateService.java
index a0d305c..3f0664d 100644
--- a/services/core/java/com/android/server/NetworkTimeUpdateService.java
+++ b/services/core/java/com/android/server/NetworkTimeUpdateService.java
@@ -30,6 +30,7 @@
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
+import android.os.PowerManager;
import android.provider.Settings;
import android.util.Log;
import android.util.NtpTrustedTime;
@@ -75,6 +76,7 @@
private SettingsObserver mSettingsObserver;
// The last time that we successfully fetched the NTP time.
private long mLastNtpFetchTime = NOT_SET;
+ private final PowerManager.WakeLock mWakeLock;
// Normal polling frequency
private final long mPollingIntervalMs;
@@ -104,6 +106,9 @@
com.android.internal.R.integer.config_ntpRetry);
mTimeErrorThresholdMs = mContext.getResources().getInteger(
com.android.internal.R.integer.config_ntpThreshold);
+
+ mWakeLock = ((PowerManager) context.getSystemService(Context.POWER_SERVICE)).newWakeLock(
+ PowerManager.PARTIAL_WAKE_LOCK, TAG);
}
/** Initialize the receivers and initiate the first NTP request */
@@ -148,7 +153,15 @@
private void onPollNetworkTime(int event) {
// If Automatic time is not set, don't bother.
if (!isAutomaticTimeRequested()) return;
+ mWakeLock.acquire();
+ try {
+ onPollNetworkTimeUnderWakeLock(event);
+ } finally {
+ mWakeLock.release();
+ }
+ }
+ private void onPollNetworkTimeUnderWakeLock(int event) {
final long refTime = SystemClock.elapsedRealtime();
// If NITZ time was received less than mPollingIntervalMs time ago,
// no need to sync to NTP.
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5aab804..9a17f2f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -19,12 +19,11 @@
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
-import static android.app.ActivityManager.DOCKED_STACK_ID;
-import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.HOME_STACK_ID;
-import static android.app.ActivityManager.INVALID_STACK_ID;
-import static android.app.ActivityManager.PINNED_STACK_ID;
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.HOME_STACK_ID;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.ActivityManager.RESIZE_MODE_PRESERVE_WINDOW;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static com.android.internal.util.XmlUtils.readBooleanAttribute;
@@ -47,6 +46,7 @@
import static org.xmlpull.v1.XmlPullParser.START_TAG;
import android.Manifest;
+import android.app.ActivityManager.StackId;
import android.app.AppOpsManager;
import android.app.ApplicationThreadNative;
import android.app.BroadcastOptions;
@@ -8362,6 +8362,9 @@
rti.affiliatedTaskId = tr.mAffiliatedTaskId;
rti.affiliatedTaskColor = tr.mAffiliatedTaskColor;
rti.numActivities = 0;
+ if (tr.mBounds != null) {
+ rti.bounds = new Rect(tr.mBounds);
+ }
ActivityRecord base = null;
ActivityRecord top = null;
@@ -8651,12 +8654,9 @@
// - a non-null bounds on a non-freeform (fullscreen OR docked) task moves
// that task to freeform
// - otherwise the task is not moved
- // Note it's not allowed to resize a home, docked, or pinned stack task.
int stackId = task.stack.mStackId;
- if (stackId == HOME_STACK_ID || stackId == DOCKED_STACK_ID
- || stackId == PINNED_STACK_ID) {
- throw new IllegalArgumentException("trying to resizeTask on a "
- + "home or docked task");
+ if (!StackId.isTaskResizeAllowed(stackId)) {
+ throw new IllegalArgumentException("resizeTask not allowed on task=" + task);
}
if (bounds == null && stackId == FREEFORM_WORKSPACE_STACK_ID) {
stackId = FULLSCREEN_WORKSPACE_STACK_ID;
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index b5f424d..4897eb9 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -16,14 +16,11 @@
package com.android.server.am;
-import static android.app.ActivityManager.DOCKED_STACK_ID;
-import static android.app.ActivityManager.FIRST_STATIC_STACK_ID;
-import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.HOME_STACK_ID;
-import static android.app.ActivityManager.INVALID_STACK_ID;
-import static android.app.ActivityManager.LAST_STATIC_STACK_ID;
-import static android.app.ActivityManager.PINNED_STACK_ID;
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.HOME_STACK_ID;
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
import static com.android.server.am.ActivityManagerDebugConfig.*;
@@ -49,6 +46,7 @@
import android.app.Activity;
import android.app.ActivityManager;
+import android.app.ActivityManager.StackId;
import android.app.ActivityOptions;
import android.app.AppGlobals;
import android.app.IActivityController;
@@ -255,9 +253,6 @@
private final LaunchingTaskPositioner mTaskPositioner;
- // If the bounds of task contained in this stack should be persisted across power cycles.
- final boolean mPersistTaskBounds;
-
static final int PAUSE_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 1;
static final int DESTROY_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 2;
static final int LAUNCH_TICK_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 3;
@@ -371,7 +366,6 @@
mRecentTasks = recentTasks;
mTaskPositioner = mStackId == FREEFORM_WORKSPACE_STACK_ID
? new LaunchingTaskPositioner() : null;
- mPersistTaskBounds = mStackId != DOCKED_STACK_ID && mStackId != PINNED_STACK_ID;
}
void attachDisplay(ActivityStackSupervisor.ActivityDisplay activityDisplay, boolean onTop) {
@@ -1365,7 +1359,7 @@
}
}
- if (mStackId >= FIRST_STATIC_STACK_ID && mStackId <= LAST_STATIC_STACK_ID) {
+ if (StackId.isStaticStack(mStackId)) {
// Visibility of any static stack should have been determined by the conditions above.
return false;
}
@@ -1377,9 +1371,7 @@
continue;
}
- if (stack.mStackId == FREEFORM_WORKSPACE_STACK_ID
- || stack.mStackId == HOME_STACK_ID
- || stack.mStackId == FULLSCREEN_WORKSPACE_STACK_ID) {
+ if (!StackId.isDynamicStacksVisibleBehindAllowed(stack.mStackId)) {
// These stacks can't have any dynamic stacks visible behind them.
return false;
}
@@ -2797,8 +2789,7 @@
ActivityRecord next = topRunningActivityLocked();
final String myReason = reason + " adjustFocus";
if (next != r) {
- if (next != null && (mStackId == FREEFORM_WORKSPACE_STACK_ID
- || mStackId == DOCKED_STACK_ID || mStackId == PINNED_STACK_ID)) {
+ if (next != null && StackId.keepFocusInStackIfPossible(mStackId)) {
// For freeform, docked, and pinned stacks we always keep the focus within the
// stack as long as there is a running activity in the stack that we can adjust
// focus to.
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 1cd71a1..7e42dce 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -18,6 +18,15 @@
import static android.Manifest.permission.START_ANY_ACTIVITY;
import static android.app.ActivityManager.*;
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.FIRST_DYNAMIC_STACK_ID;
+import static android.app.ActivityManager.StackId.FIRST_STATIC_STACK_ID;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.HOME_STACK_ID;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.ActivityManager.StackId.LAST_STATIC_STACK_ID;
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
@@ -40,6 +49,7 @@
import android.Manifest;
import android.app.Activity;
import android.app.ActivityManager;
+import android.app.ActivityManager.StackId;
import android.app.ActivityManager.StackInfo;
import android.app.ActivityOptions;
import android.app.AppGlobals;
@@ -554,8 +564,8 @@
* @param id Id of the task we would like returned.
* @param restoreFromRecents If the id was not in the active list, but was found in recents,
* restore the task from recents to the active list.
- * @param stackId The stack to restore the task to (default launch stack will be used
- * if stackId is {@link android.app.ActivityManager#INVALID_STACK_ID}).
+ * @param stackId The stack to restore the task to (default launch stack will be used if
+ * stackId is {@link android.app.ActivityManager.StackId#INVALID_STACK_ID}).
*/
TaskRecord anyTaskForIdLocked(int id, boolean restoreFromRecents, int stackId) {
int numDisplays = mActivityDisplays.size();
@@ -1820,8 +1830,7 @@
final ArrayList<ActivityStack> homeDisplayStacks = mHomeStack.mStacks;
for (int stackNdx = homeDisplayStacks.size() - 1; stackNdx >= 0; --stackNdx) {
stack = homeDisplayStacks.get(stackNdx);
- final boolean isDynamicStack = stack.mStackId >= FIRST_DYNAMIC_STACK_ID;
- if (isDynamicStack) {
+ if (!StackId.isStaticStack(stack.mStackId)) {
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
"computeStackFocus: Setting focused stack=" + stack);
return stack;
@@ -2899,15 +2908,14 @@
if (activityContainer != null) {
return activityContainer.mStack;
}
- if (!createStaticStackIfNeeded
- || (stackId < FIRST_STATIC_STACK_ID || stackId > LAST_STATIC_STACK_ID)) {
+ if (!createStaticStackIfNeeded || !StackId.isStaticStack(stackId)) {
return null;
}
return createStackOnDisplay(stackId, Display.DEFAULT_DISPLAY, createOnTop);
}
ArrayList<ActivityStack> getStacks() {
- ArrayList<ActivityStack> allStacks = new ArrayList<ActivityStack>();
+ ArrayList<ActivityStack> allStacks = new ArrayList<>();
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
allStacks.addAll(mActivityDisplays.valueAt(displayNdx).mStacks);
}
@@ -3025,7 +3033,7 @@
// In this case we make all other static stacks fullscreen and move all
// docked stack tasks to the fullscreen stack.
for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
- if (i != DOCKED_STACK_ID && getStack(i) != null) {
+ if (StackId.isResizeableByDockedStack(i) && getStack(i) != null) {
resizeStackLocked(i, null, preserveWindows, true);
}
}
@@ -3046,7 +3054,7 @@
mWindowManager.getStackDockedModeBounds(HOME_STACK_ID, tempRect);
for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
- if (i != DOCKED_STACK_ID) {
+ if (StackId.isResizeableByDockedStack(i)) {
ActivityStack otherStack = getStack(i);
if (otherStack != null) {
resizeStackLocked(i, tempRect, PRESERVE_WINDOWS, true);
@@ -3190,7 +3198,7 @@
* Restores a recent task to a stack
* @param task The recent task to be restored.
* @param stackId The stack to restore the task to (default launch stack will be used
- * if stackId is {@link android.app.ActivityManager#INVALID_STACK_ID}).
+ * if stackId is {@link android.app.ActivityManager.StackId#INVALID_STACK_ID}).
* @return true if the task has been restored successfully.
*/
private boolean restoreRecentTaskLocked(TaskRecord task, int stackId) {
@@ -3275,8 +3283,7 @@
Slog.w(TAG, "moveTaskToStack: no task for id=" + taskId);
return;
}
- if (stackId == DOCKED_STACK_ID || stackId == PINNED_STACK_ID
- || stackId == FULLSCREEN_WORKSPACE_STACK_ID) {
+ if (StackId.preserveWindowOnTaskMove(stackId)) {
// We are about to relaunch the activity because its configuration changed due to
// being maximized, i.e. size change. The activity will first remove the old window
// and then add a new one. This call will tell window manager about this, so it can
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 120b40c..5fd213f 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -16,11 +16,9 @@
package com.android.server.am;
-import static android.app.ActivityManager.DOCKED_STACK_ID;
-import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.HOME_STACK_ID;
-import static android.app.ActivityManager.PINNED_STACK_ID;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.HOME_STACK_ID;
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
@@ -35,6 +33,7 @@
import android.app.Activity;
import android.app.ActivityManager;
+import android.app.ActivityManager.StackId;
import android.app.ActivityManager.TaskThumbnail;
import android.app.ActivityManager.TaskDescription;
import android.app.ActivityOptions;
@@ -1193,14 +1192,14 @@
mFullscreen = bounds == null;
if (mFullscreen) {
- if (mBounds != null && stack.mPersistTaskBounds) {
+ if (mBounds != null && StackId.persistTaskBounds(stack.mStackId)) {
mLastNonFullscreenBounds = mBounds;
}
mBounds = null;
mOverrideConfig = Configuration.EMPTY;
} else {
mBounds = new Rect(bounds);
- if (stack == null || stack.mPersistTaskBounds) {
+ if (stack == null || StackId.persistTaskBounds(stack.mStackId)) {
mLastNonFullscreenBounds = mBounds;
}
@@ -1244,7 +1243,7 @@
|| stackId == HOME_STACK_ID
|| stackId == FULLSCREEN_WORKSPACE_STACK_ID) {
return (mResizeable && stack != null) ? stack.mBounds : null;
- } else if (!stack.mPersistTaskBounds) {
+ } else if (!StackId.persistTaskBounds(stackId)) {
return stack.mBounds;
}
return mLastNonFullscreenBounds;
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 66e731a..d89f47c 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1756,7 +1756,8 @@
if (uid == android.os.Process.SYSTEM_UID) {
uid = UserHandle.getUid(userId, UserHandle.getAppId(uid));
}
- if (mAppOps.noteOp(AppOpsManager.OP_AUDIO_MASTER_VOLUME, uid, callingPackage)
+ // If OP_AUDIO_MASTER_VOLUME is set, disallow unmuting.
+ if (!mute && mAppOps.noteOp(AppOpsManager.OP_AUDIO_MASTER_VOLUME, uid, callingPackage)
!= AppOpsManager.MODE_ALLOWED) {
return;
}
@@ -1848,7 +1849,8 @@
if (uid == android.os.Process.SYSTEM_UID) {
uid = UserHandle.getUid(userId, UserHandle.getAppId(uid));
}
- if (mAppOps.noteOp(AppOpsManager.OP_MUTE_MICROPHONE, uid, callingPackage)
+ // If OP_MUTE_MICROPHONE is set, disallow unmuting.
+ if (!on && mAppOps.noteOp(AppOpsManager.OP_MUTE_MICROPHONE, uid, callingPackage)
!= AppOpsManager.MODE_ALLOWED) {
return;
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 22bedc3..4863603 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -162,6 +162,7 @@
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.os.SELinux;
import android.os.ServiceManager;
import android.os.SystemClock;
@@ -15086,6 +15087,13 @@
}
@Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out,
+ FileDescriptor err, String[] args, ResultReceiver resultReceiver) {
+ (new PackageManagerShellCommand(this)).exec(
+ this, in, out, err, args, resultReceiver);
+ }
+
+ @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
new file mode 100644
index 0000000..c259ac2
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -0,0 +1,529 @@
+package com.android.server.pm;
+
+import android.content.ComponentName;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.FeatureInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.InstrumentationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
+import android.content.pm.PermissionGroupInfo;
+import android.content.pm.PermissionInfo;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.os.RemoteException;
+import android.os.ShellCommand;
+import android.os.UserHandle;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.WeakHashMap;
+
+class PackageManagerShellCommand extends ShellCommand {
+ final IPackageManager mInterface;
+ final private WeakHashMap<String, Resources> mResourceCache =
+ new WeakHashMap<String, Resources>();
+
+ PackageManagerShellCommand(PackageManagerService service) {
+ mInterface = service;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
+
+ final PrintWriter pw = getOutPrintWriter();
+ try {
+ switch(cmd) {
+ case "list":
+ return runList();
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ } catch (RemoteException e) {
+ pw.println("Remote exception: " + e);
+ }
+ return -1;
+ }
+
+ private int runList() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ final String type = getNextArg();
+ if (type == null) {
+ pw.println("Error: didn't specify type of data to list");
+ return -1;
+ }
+ switch(type) {
+ case "features":
+ return runListFeatures();
+ case "instrumentation":
+ return runListInstrumentation();
+ case "libraries":
+ return runListLibraries();
+ case "package":
+ case "packages":
+ return runListPackages(false /*showSourceDir*/);
+ case "permission-groups":
+ return runListPermissionGroups();
+ case "permissions":
+ return runListPermissions();
+ }
+ pw.println("Error: unknown list type '" + type + "'");
+ return -1;
+ }
+
+ private int runListFeatures() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ final List<FeatureInfo> list = new ArrayList<FeatureInfo>();
+ final FeatureInfo[] rawList = mInterface.getSystemAvailableFeatures();
+ for (int i=0; i<rawList.length; i++) {
+ list.add(rawList[i]);
+ }
+
+ // sort by name
+ Collections.sort(list, new Comparator<FeatureInfo>() {
+ public int compare(FeatureInfo o1, FeatureInfo o2) {
+ if (o1.name == o2.name) return 0;
+ if (o1.name == null) return -1;
+ if (o2.name == null) return 1;
+ return o1.name.compareTo(o2.name);
+ }
+ });
+
+ final int count = (list != null) ? list.size() : 0;
+ for (int p = 0; p < count; p++) {
+ FeatureInfo fi = list.get(p);
+ pw.print("feature:");
+ if (fi.name != null) pw.println(fi.name);
+ else pw.println("reqGlEsVersion=0x"
+ + Integer.toHexString(fi.reqGlEsVersion));
+ }
+ return 0;
+ }
+
+ private int runListInstrumentation() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ boolean showSourceDir = false;
+ String targetPackage = null;
+
+ try {
+ String opt;
+ while ((opt = getNextArg()) != null) {
+ switch (opt) {
+ case "-f":
+ showSourceDir = true;
+ break;
+ default:
+ if (opt.charAt(0) != '-') {
+ targetPackage = opt;
+ } else {
+ pw.println("Error: Unknown option: " + opt);
+ return -1;
+ }
+ break;
+ }
+ }
+ } catch (RuntimeException ex) {
+ pw.println("Error: " + ex.toString());
+ return -1;
+ }
+
+ final List<InstrumentationInfo> list =
+ mInterface.queryInstrumentation(targetPackage, 0 /*flags*/);
+
+ // sort by target package
+ Collections.sort(list, new Comparator<InstrumentationInfo>() {
+ public int compare(InstrumentationInfo o1, InstrumentationInfo o2) {
+ return o1.targetPackage.compareTo(o2.targetPackage);
+ }
+ });
+
+ final int count = (list != null) ? list.size() : 0;
+ for (int p = 0; p < count; p++) {
+ final InstrumentationInfo ii = list.get(p);
+ pw.print("instrumentation:");
+ if (showSourceDir) {
+ pw.print(ii.sourceDir);
+ pw.print("=");
+ }
+ final ComponentName cn = new ComponentName(ii.packageName, ii.name);
+ pw.print(cn.flattenToShortString());
+ pw.print(" (target=");
+ pw.print(ii.targetPackage);
+ pw.println(")");
+ }
+ return 0;
+ }
+
+ private int runListLibraries() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ final List<String> list = new ArrayList<String>();
+ final String[] rawList = mInterface.getSystemSharedLibraryNames();
+ for (int i = 0; i < rawList.length; i++) {
+ list.add(rawList[i]);
+ }
+
+ // sort by name
+ Collections.sort(list, new Comparator<String>() {
+ public int compare(String o1, String o2) {
+ if (o1 == o2) return 0;
+ if (o1 == null) return -1;
+ if (o2 == null) return 1;
+ return o1.compareTo(o2);
+ }
+ });
+
+ final int count = (list != null) ? list.size() : 0;
+ for (int p = 0; p < count; p++) {
+ String lib = list.get(p);
+ pw.print("library:");
+ pw.println(lib);
+ }
+ return 0;
+ }
+
+ private int runListPackages(boolean showSourceDir) throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ int getFlags = 0;
+ boolean listDisabled = false, listEnabled = false;
+ boolean listSystem = false, listThirdParty = false;
+ boolean listInstaller = false;
+ int userId = UserHandle.USER_SYSTEM;
+ try {
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "-d":
+ listDisabled = true;
+ break;
+ case "-e":
+ listEnabled = true;
+ break;
+ case "-f":
+ showSourceDir = true;
+ break;
+ case "-i":
+ listInstaller = true;
+ break;
+ case "-l":
+ // old compat
+ break;
+ case "-lf":
+ showSourceDir = true;
+ break;
+ case "-s":
+ listSystem = true;
+ break;
+ case "-u":
+ getFlags |= PackageManager.GET_UNINSTALLED_PACKAGES;
+ break;
+ case "-3":
+ listThirdParty = true;
+ break;
+ case "--user":
+ userId = Integer.parseInt(getNextArg());
+ break;
+ default:
+ pw.println("Error: Unknown option: " + opt);
+ return -1;
+ }
+ }
+ } catch (RuntimeException ex) {
+ pw.println("Error: " + ex.toString());
+ return -1;
+ }
+
+ final String filter = getNextArg();
+
+ @SuppressWarnings("unchecked")
+ final ParceledListSlice<PackageInfo> slice =
+ mInterface.getInstalledPackages(getFlags, userId);
+ final List<PackageInfo> packages = slice.getList();
+
+ final int count = packages.size();
+ for (int p = 0; p < count; p++) {
+ final PackageInfo info = packages.get(p);
+ if (filter != null && !info.packageName.contains(filter)) {
+ continue;
+ }
+ final boolean isSystem =
+ (info.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0;
+ if ((!listDisabled || !info.applicationInfo.enabled) &&
+ (!listEnabled || info.applicationInfo.enabled) &&
+ (!listSystem || isSystem) &&
+ (!listThirdParty || !isSystem)) {
+ pw.print("package:");
+ if (showSourceDir) {
+ pw.print(info.applicationInfo.sourceDir);
+ pw.print("=");
+ }
+ pw.print(info.packageName);
+ if (listInstaller) {
+ pw.print(" installer=");
+ pw.print(mInterface.getInstallerPackageName(info.packageName));
+ }
+ pw.println();
+ }
+ }
+ return 0;
+ }
+
+ private int runListPermissionGroups() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ final List<PermissionGroupInfo> pgs = mInterface.getAllPermissionGroups(0);
+
+ final int count = pgs.size();
+ for (int p = 0; p < count ; p++) {
+ final PermissionGroupInfo pgi = pgs.get(p);
+ pw.print("permission group:");
+ pw.println(pgi.name);
+ }
+ return 0;
+ }
+
+ private int runListPermissions() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ boolean labels = false;
+ boolean groups = false;
+ boolean userOnly = false;
+ boolean summary = false;
+ boolean dangerousOnly = false;
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "-d":
+ dangerousOnly = true;
+ break;
+ case "-f":
+ labels = true;
+ break;
+ case "-g":
+ groups = true;
+ break;
+ case "-s":
+ groups = true;
+ labels = true;
+ summary = true;
+ break;
+ case "-u":
+ userOnly = true;
+ break;
+ default:
+ pw.println("Error: Unknown option: " + opt);
+ return 1;
+ }
+ }
+
+ final ArrayList<String> groupList = new ArrayList<String>();
+ if (groups) {
+ final List<PermissionGroupInfo> infos =
+ mInterface.getAllPermissionGroups(0 /*flags*/);
+ final int count = infos.size();
+ for (int i = 0; i < count; i++) {
+ groupList.add(infos.get(i).name);
+ }
+ groupList.add(null);
+ } else {
+ final String grp = getNextArg();
+ groupList.add(grp);
+ }
+
+ if (dangerousOnly) {
+ pw.println("Dangerous Permissions:");
+ pw.println("");
+ doListPermissions(groupList, groups, labels, summary,
+ PermissionInfo.PROTECTION_DANGEROUS,
+ PermissionInfo.PROTECTION_DANGEROUS);
+ if (userOnly) {
+ pw.println("Normal Permissions:");
+ pw.println("");
+ doListPermissions(groupList, groups, labels, summary,
+ PermissionInfo.PROTECTION_NORMAL,
+ PermissionInfo.PROTECTION_NORMAL);
+ }
+ } else if (userOnly) {
+ pw.println("Dangerous and Normal Permissions:");
+ pw.println("");
+ doListPermissions(groupList, groups, labels, summary,
+ PermissionInfo.PROTECTION_NORMAL,
+ PermissionInfo.PROTECTION_DANGEROUS);
+ } else {
+ pw.println("All Permissions:");
+ pw.println("");
+ doListPermissions(groupList, groups, labels, summary,
+ -10000, 10000);
+ }
+ return 0;
+ }
+
+ private void doListPermissions(ArrayList<String> groupList, boolean groups, boolean labels,
+ boolean summary, int startProtectionLevel, int endProtectionLevel)
+ throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ final int groupCount = groupList.size();
+ for (int i = 0; i < groupCount; i++) {
+ String groupName = groupList.get(i);
+ String prefix = "";
+ if (groups) {
+ if (i > 0) {
+ pw.println("");
+ }
+ if (groupName != null) {
+ PermissionGroupInfo pgi =
+ mInterface.getPermissionGroupInfo(groupName, 0 /*flags*/);
+ if (summary) {
+ Resources res = getResources(pgi);
+ if (res != null) {
+ pw.print(loadText(pgi, pgi.labelRes, pgi.nonLocalizedLabel) + ": ");
+ } else {
+ pw.print(pgi.name + ": ");
+
+ }
+ } else {
+ pw.println((labels ? "+ " : "") + "group:" + pgi.name);
+ if (labels) {
+ pw.println(" package:" + pgi.packageName);
+ Resources res = getResources(pgi);
+ if (res != null) {
+ pw.println(" label:"
+ + loadText(pgi, pgi.labelRes, pgi.nonLocalizedLabel));
+ pw.println(" description:"
+ + loadText(pgi, pgi.descriptionRes,
+ pgi.nonLocalizedDescription));
+ }
+ }
+ }
+ } else {
+ pw.println(((labels && !summary) ? "+ " : "") + "ungrouped:");
+ }
+ prefix = " ";
+ }
+ List<PermissionInfo> ps =
+ mInterface.queryPermissionsByGroup(groupList.get(i), 0 /*flags*/);
+ final int count = ps.size();
+ boolean first = true;
+ for (int p = 0 ; p < count ; p++) {
+ PermissionInfo pi = ps.get(p);
+ if (groups && groupName == null && pi.group != null) {
+ continue;
+ }
+ final int base = pi.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
+ if (base < startProtectionLevel
+ || base > endProtectionLevel) {
+ continue;
+ }
+ if (summary) {
+ if (first) {
+ first = false;
+ } else {
+ pw.print(", ");
+ }
+ Resources res = getResources(pi);
+ if (res != null) {
+ pw.print(loadText(pi, pi.labelRes,
+ pi.nonLocalizedLabel));
+ } else {
+ pw.print(pi.name);
+ }
+ } else {
+ pw.println(prefix + (labels ? "+ " : "")
+ + "permission:" + pi.name);
+ if (labels) {
+ pw.println(prefix + " package:" + pi.packageName);
+ Resources res = getResources(pi);
+ if (res != null) {
+ pw.println(prefix + " label:"
+ + loadText(pi, pi.labelRes,
+ pi.nonLocalizedLabel));
+ pw.println(prefix + " description:"
+ + loadText(pi, pi.descriptionRes,
+ pi.nonLocalizedDescription));
+ }
+ pw.println(prefix + " protectionLevel:"
+ + PermissionInfo.protectionToString(pi.protectionLevel));
+ }
+ }
+ }
+
+ if (summary) {
+ pw.println("");
+ }
+ }
+ }
+
+ private String loadText(PackageItemInfo pii, int res, CharSequence nonLocalized)
+ throws RemoteException {
+ if (nonLocalized != null) {
+ return nonLocalized.toString();
+ }
+ if (res != 0) {
+ Resources r = getResources(pii);
+ if (r != null) {
+ try {
+ return r.getString(res);
+ } catch (Resources.NotFoundException e) {
+ }
+ }
+ }
+ return null;
+ }
+
+ private Resources getResources(PackageItemInfo pii) throws RemoteException {
+ Resources res = mResourceCache.get(pii.packageName);
+ if (res != null) return res;
+
+ ApplicationInfo ai = mInterface.getApplicationInfo(pii.packageName, 0, 0);
+ AssetManager am = new AssetManager();
+ am.addAssetPath(ai.publicSourceDir);
+ res = new Resources(am, null, null);
+ mResourceCache.put(pii.packageName, res);
+ return res;
+ }
+
+ @Override
+ public void onHelp() {
+ final PrintWriter pw = getOutPrintWriter();
+ pw.println("Package manager (package) commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println("");
+ pw.println(" list features");
+ pw.println(" Prints all features of the system.");
+ pw.println(" list instrumentation [-f] [TARGET-PACKAGE]");
+ pw.println(" Prints all test packages; optionally only those targetting TARGET-PACKAGE");
+ pw.println(" Options:");
+ pw.println(" -f: dump the name of the .apk file containing the test package");
+ pw.println(" list libraries");
+ pw.println(" Prints all system libraries.");
+ pw.println(" list packages [-f] [-d] [-e] [-s] [-3] [-i] [-u] [--user USER_ID] [FILTER]");
+ pw.println(" Prints all packages; optionally only those whose name contains");
+ pw.println(" the text in FILTER.");
+ pw.println(" Options:");
+ pw.println(" -f: see their associated file");
+ pw.println(" -d: filter to only show disbled packages");
+ pw.println(" -e: filter to only show enabled packages");
+ pw.println(" -s: filter to only show system packages");
+ pw.println(" -3: filter to only show third party packages");
+ pw.println(" -i: see the installer for the packages");
+ pw.println(" -u: also include uninstalled packages");
+ pw.println(" list permission-groups");
+ pw.println(" Prints all known permission groups.");
+ pw.println(" list permissions [-g] [-f] [-d] [-u] [GROUP]");
+ pw.println(" Prints all known permissions; optionally only those in GROUP.");
+ pw.println(" Options:");
+ pw.println(" -g: organize by group");
+ pw.println(" -f: print all information");
+ pw.println(" -s: short summary");
+ pw.println(" -d: only list dangerous permissions");
+ pw.println(" -u: list only the permissions users will see");
+ pw.println("");
+ }
+}
+
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index bbdfe31..78328f5 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -239,6 +239,7 @@
keySetData = base.keySetData;
verificationInfo = base.verificationInfo;
installerPackageName = base.installerPackageName;
+ volumeUuid = base.volumeUuid;
}
private PackageUserState modifyUserState(int userId) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 5d0e83d..925acb8 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -22,6 +22,7 @@
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.ActivityManagerNative;
+import android.app.IActivityManager;
import android.app.IStopUserCallback;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManagerInternal;
@@ -44,7 +45,9 @@
import android.os.Parcelable;
import android.os.Process;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.os.ServiceManager;
+import android.os.ShellCommand;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
@@ -199,6 +202,14 @@
@GuardedBy("mRestrictionsLock")
private final SparseArray<Bundle> mCachedEffectiveUserRestrictions = new SparseArray<>();
+ /**
+ * User restrictions that have already been applied in {@link #applyUserRestrictionsRL}. We
+ * use it to detect restrictions that have changed since the last
+ * {@link #applyUserRestrictionsRL} call.
+ */
+ @GuardedBy("mRestrictionsLock")
+ private final SparseArray<Bundle> mAppliedUserRestrictions = new SparseArray<>();
+
private final Bundle mGuestRestrictions = new Bundle();
/**
@@ -727,8 +738,6 @@
Log.d(LOG_TAG, "updateUserRestrictionsInternalLocked userId=" + userId
+ " bundle=" + newRestrictions);
}
- final Bundle prevRestrictions = getEffectiveUserRestrictions(userId);
-
// Update system restrictions.
if (newRestrictions != null) {
// If newRestrictions == the current one, it's probably a bug.
@@ -738,15 +747,22 @@
mBaseUserRestrictions.put(userId, newRestrictions);
}
- mCachedEffectiveUserRestrictions.put(
- userId, computeEffectiveUserRestrictionsRL(userId));
+ final Bundle effective = computeEffectiveUserRestrictionsRL(userId);
- applyUserRestrictionsRL(userId, mBaseUserRestrictions.get(userId), prevRestrictions);
+ mCachedEffectiveUserRestrictions.put(userId, effective);
+
+ applyUserRestrictionsRL(userId, effective);
}
@GuardedBy("mRestrictionsLock")
- private void applyUserRestrictionsRL(int userId,
- Bundle newRestrictions, Bundle prevRestrictions) {
+ private void applyUserRestrictionsRL(int userId, Bundle newRestrictions) {
+ final Bundle prevRestrictions = mAppliedUserRestrictions.get(userId);
+
+ if (DBG) {
+ Log.d(LOG_TAG, "applyUserRestrictionsRL userId=" + userId
+ + " new=" + newRestrictions + " prev=" + prevRestrictions);
+ }
+
final long token = Binder.clearCallingIdentity();
try {
mAppOpsService.setUserRestrictions(newRestrictions, userId);
@@ -756,7 +772,10 @@
Binder.restoreCallingIdentity(token);
}
- // TODO Move the code from DPMS.setUserRestriction().
+ UserRestrictionsUtils.applyUserRestrictions(
+ mContext, userId, newRestrictions, prevRestrictions);
+
+ mAppliedUserRestrictions.put(userId, new Bundle(newRestrictions));
}
@GuardedBy("mRestrictionsLock")
@@ -767,9 +786,8 @@
@GuardedBy("mRestrictionsLock")
private void updateEffectiveUserRestrictionsForAllUsersRL() {
// First, invalidate all cached values.
- synchronized (mRestrictionsLock) {
- mCachedEffectiveUserRestrictions.clear();
- }
+ mCachedEffectiveUserRestrictions.clear();
+
// We don't want to call into ActivityManagerNative while taking a lock, so we'll call
// it on a handler.
final Runnable r = new Runnable() {
@@ -2150,6 +2168,45 @@
}
@Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out,
+ FileDescriptor err, String[] args, ResultReceiver resultReceiver) {
+ (new Shell()).exec(this, in, out, err, args, resultReceiver);
+ }
+
+ int onShellCommand(Shell shell, String cmd) {
+ if (cmd == null) {
+ return shell.handleDefaultCommands(cmd);
+ }
+
+ final PrintWriter pw = shell.getOutPrintWriter();
+ try {
+ switch(cmd) {
+ case "list":
+ return runList(pw);
+ }
+ } catch (RemoteException e) {
+ pw.println("Remote exception: " + e);
+ }
+ return -1;
+ }
+
+ private int runList(PrintWriter pw) throws RemoteException {
+ final IActivityManager am = ActivityManagerNative.getDefault();
+ final List<UserInfo> users = getUsers(false);
+ if (users == null) {
+ pw.println("Error: couldn't get users");
+ return 1;
+ } else {
+ pw.println("Users:");
+ for (int i = 0; i < users.size(); i++) {
+ String running = am.isUserRunning(users.get(i).id, false) ? " running" : "";
+ pw.println("\t" + users.get(i).toString() + running);
+ }
+ return 0;
+ }
+ }
+
+ @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
@@ -2273,4 +2330,22 @@
}
}
}
+
+ private class Shell extends ShellCommand {
+ @Override
+ public int onCommand(String cmd) {
+ return onShellCommand(this, cmd);
+ }
+
+ @Override
+ public void onHelp() {
+ final PrintWriter pw = getOutPrintWriter();
+ pw.println("User manager (user) commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println("");
+ pw.println(" list");
+ pw.println(" Prints all users on the system.");
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 23e3b35..28df9f6 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -18,10 +18,19 @@
import com.google.android.collect.Sets;
-import com.android.internal.util.Preconditions;
-
+import android.annotation.Nullable;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.media.IAudioService;
+import android.net.Uri;
+import android.os.Binder;
import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemProperties;
+import android.os.UserHandle;
import android.os.UserManager;
+import android.util.Slog;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlSerializer;
@@ -31,6 +40,8 @@
import java.util.Set;
public class UserRestrictionsUtils {
+ private static final String TAG = "UserRestrictionsUtils";
+
private UserRestrictionsUtils() {
}
@@ -115,6 +126,118 @@
}
}
+ /**
+ * Takes a new use restriction set and the previous set, and apply the restrictions that have
+ * changed.
+ */
+ public static void applyUserRestrictions(Context context, int userId,
+ @Nullable Bundle newRestrictions, @Nullable Bundle prevRestrictions) {
+ if (newRestrictions == null) {
+ newRestrictions = Bundle.EMPTY;
+ }
+ if (prevRestrictions == null) {
+ prevRestrictions = Bundle.EMPTY;
+ }
+ for (String key : USER_RESTRICTIONS) {
+ final boolean newValue = newRestrictions.getBoolean(key);
+ final boolean prevValue = prevRestrictions.getBoolean(key);
+
+ if (newValue != prevValue) {
+ applyUserRestriction(context, userId, key, newValue);
+ }
+ }
+ }
+
+ private static void applyUserRestriction(Context context, int userId, String key,
+ boolean newValue) {
+ // When certain restrictions are cleared, we don't update the system settings,
+ // because these settings are changeable on the Settings UI and we don't know the original
+ // value -- for example LOCATION_MODE might have been off already when the restriction was
+ // set, and in that case even if the restriction is lifted, changing it to ON would be
+ // wrong. So just don't do anything in such a case. If the user hopes to enable location
+ // later, they can do it on the Settings UI.
+
+ final ContentResolver cr = context.getContentResolver();
+ final long id = Binder.clearCallingIdentity();
+ try {
+ switch (key) {
+ case UserManager.DISALLOW_UNMUTE_MICROPHONE:
+ IAudioService.Stub.asInterface(ServiceManager.getService(Context.AUDIO_SERVICE))
+ .setMicrophoneMute(newValue, context.getPackageName(), userId);
+ break;
+ case UserManager.DISALLOW_ADJUST_VOLUME:
+ IAudioService.Stub.asInterface(ServiceManager.getService(Context.AUDIO_SERVICE))
+ .setMasterMute(newValue, 0, context.getPackageName(), userId);
+ break;
+ case UserManager.DISALLOW_CONFIG_WIFI:
+ if (newValue) {
+ android.provider.Settings.Secure.putIntForUser(cr,
+ android.provider.Settings.Secure
+ .WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 0, userId);
+ }
+ break;
+ case UserManager.DISALLOW_SHARE_LOCATION:
+ if (newValue) {
+ android.provider.Settings.Secure.putIntForUser(cr,
+ android.provider.Settings.Secure.LOCATION_MODE,
+ android.provider.Settings.Secure.LOCATION_MODE_OFF,
+ userId);
+ android.provider.Settings.Secure.putStringForUser(cr,
+ android.provider.Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "",
+ userId);
+ }
+ // Send out notifications as some clients may want to reread the
+ // value which actually changed due to a restriction having been
+ // applied.
+ final String property =
+ android.provider.Settings.Secure.SYS_PROP_SETTING_VERSION;
+ long version = SystemProperties.getLong(property, 0) + 1;
+ SystemProperties.set(property, Long.toString(version));
+
+ final String name = android.provider.Settings.Secure.LOCATION_PROVIDERS_ALLOWED;
+ final Uri url = Uri.withAppendedPath(
+ android.provider.Settings.Secure.CONTENT_URI, name);
+ context.getContentResolver().notifyChange(url, null, true, userId);
+
+ break;
+ case UserManager.DISALLOW_DEBUGGING_FEATURES:
+ if (newValue) {
+ // Only disable adb if changing for system user, since it is global
+ // TODO: should this be admin user?
+ if (userId == UserHandle.USER_SYSTEM) {
+ android.provider.Settings.Global.putStringForUser(cr,
+ android.provider.Settings.Global.ADB_ENABLED, "0",
+ userId);
+ }
+ }
+ break;
+ case UserManager.ENSURE_VERIFY_APPS:
+ if (newValue) {
+ android.provider.Settings.Global.putStringForUser(
+ context.getContentResolver(),
+ android.provider.Settings.Global.PACKAGE_VERIFIER_ENABLE, "1",
+ userId);
+ android.provider.Settings.Global.putStringForUser(
+ context.getContentResolver(),
+ android.provider.Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, "1",
+ userId);
+ }
+ break;
+ case UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES:
+ if (newValue) {
+ android.provider.Settings.Secure.putIntForUser(cr,
+ android.provider.Settings.Secure.INSTALL_NON_MARKET_APPS, 0,
+ userId);
+ }
+ break;
+ }
+ } catch (RemoteException re) {
+ Slog.e(TAG, "Failed to talk to AudioService.", re);
+ } finally {
+ Binder.restoreCallingIdentity(id);
+ }
+ }
+
public static void dumpRestrictions(PrintWriter pw, String prefix, Bundle restrictions) {
boolean noneSet = true;
if (restrictions != null) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index b38b9ce..209751e 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -16,8 +16,8 @@
package com.android.server.policy;
-import static android.app.ActivityManager.DOCKED_STACK_ID;
-import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.view.WindowManager.LayoutParams.*;
import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
index d4c5f87..ac79b36 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -40,6 +40,8 @@
private boolean mRelroReady32Bit = false;
private boolean mRelroReady64Bit = false;
+ private String oldWebViewPackageName = null;
+
private BroadcastReceiver mWebViewUpdatedReceiver;
public WebViewUpdateService(Context context) {
@@ -51,9 +53,22 @@
mWebViewUpdatedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- String webviewPackage = "package:" + WebViewFactory.getWebViewPackageName();
- if (webviewPackage.equals(intent.getDataString())) {
- onWebViewUpdateInstalled();
+
+ for (String packageName : WebViewFactory.getWebViewPackageNames()) {
+ String webviewPackage = "package:" + packageName;
+
+ if (webviewPackage.equals(intent.getDataString())) {
+ String usedPackageName =
+ WebViewFactory.findPreferredWebViewPackage().packageName;
+ // Only trigger update actions if the updated package is the one that
+ // will be used, or the one that was in use before the update.
+ if (packageName.equals(usedPackageName) ||
+ packageName.equals(oldWebViewPackageName)) {
+ onWebViewUpdateInstalled();
+ oldWebViewPackageName = usedPackageName;
+ }
+ return;
+ }
}
}
};
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index fab8ee5..d6f807e 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -16,14 +16,15 @@
package com.android.server.wm;
-import static android.app.ActivityManager.DOCKED_STACK_ID;
-import static android.app.ActivityManager.HOME_STACK_ID;
-import static android.app.ActivityManager.PINNED_STACK_ID;
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.HOME_STACK_ID;
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static com.android.server.wm.WindowManagerService.DEBUG_VISIBILITY;
import static com.android.server.wm.WindowManagerService.TAG;
import static com.android.server.wm.WindowState.RESIZE_HANDLE_WIDTH_IN_DP;
import static com.android.server.wm.WindowState.BOUNDS_FOR_TOUCH;
+import android.app.ActivityManager.StackId;
import android.graphics.Rect;
import android.graphics.Region;
import android.util.DisplayMetrics;
@@ -311,7 +312,7 @@
final int delta = mService.dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, mDisplayMetrics);
for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
TaskStack stack = mStacks.get(stackNdx);
- if (!stack.allowTaskResize()) {
+ if (!StackId.isTaskResizeAllowed(stack.mStackId)) {
break;
}
final ArrayList<Task> tasks = stack.getTasks();
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 04cba81..eafc3c6 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -16,7 +16,7 @@
package com.android.server.wm;
-import static android.app.ActivityManager.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.view.PointerIcon.STYLE_HORIZONTAL_DOUBLE_ARROW;
import static android.view.PointerIcon.STYLE_VERTICAL_DOUBLE_ARROW;
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 1f62bc1..c47c377 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -187,6 +187,12 @@
mService.removeWindow(this, window);
}
+ @Override
+ public void repositionChild(IWindow window, int x, int y, long deferTransactionUntilFrame,
+ Rect outFrame) {
+ mService.repositionChild(this, window, x, y, deferTransactionUntilFrame, outFrame);
+ }
+
public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewFlags,
int flags, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index ad44196..dbf13fe 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -16,22 +16,21 @@
package com.android.server.wm;
-import static android.app.ActivityManager.DOCKED_STACK_ID;
-import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.HOME_STACK_ID;
-import static android.app.ActivityManager.PINNED_STACK_ID;
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.HOME_STACK_ID;
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.app.ActivityManager.RESIZE_MODE_SYSTEM_SCREEN_ROTATION;
import static com.android.server.wm.WindowManagerService.TAG;
import static com.android.server.wm.WindowManagerService.DEBUG_RESIZE;
import static com.android.server.wm.WindowManagerService.DEBUG_STACK;
import static com.android.server.wm.WindowManagerService.H.RESIZE_TASK;
-
+import android.app.ActivityManager.StackId;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.util.EventLog;
import android.util.Slog;
-import android.util.SparseArray;
import android.view.DisplayInfo;
import android.view.Surface;
@@ -245,7 +244,7 @@
private boolean useCurrentBounds() {
final DisplayContent displayContent = mStack.getDisplayContent();
if (mFullscreen
- || mStack.allowTaskResize()
+ || !StackId.isTaskResizeableByDockedStack(mStack.mStackId)
|| displayContent == null
|| displayContent.getDockedStackLocked() != null) {
return true;
@@ -320,6 +319,15 @@
}
}
+ /**
+ * Cancels any running app transitions associated with the task.
+ */
+ void cancelTaskWindowTransition() {
+ for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) {
+ mAppTokens.get(activityNdx).mAppAnimator.clearAnimation();
+ }
+ }
+
boolean showForAllUsers() {
final int tokensCount = mAppTokens.size();
return (tokensCount != 0) && mAppTokens.get(tokensCount - 1).showForAllUsers;
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 227b3f0..aae3bd2 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -18,7 +18,7 @@
import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
-import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.RESIZE_MODE_USER;
import static android.app.ActivityManager.RESIZE_MODE_USER_FORCED;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index df664bd..95e2391 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -17,17 +17,16 @@
package com.android.server.wm;
import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
-import static android.app.ActivityManager.DOCKED_STACK_ID;
-import static android.app.ActivityManager.FIRST_STATIC_STACK_ID;
-import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.LAST_STATIC_STACK_ID;
-import static android.app.ActivityManager.PINNED_STACK_ID;
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static com.android.server.wm.WindowManagerService.DEBUG_TASK_MOVEMENT;
import static com.android.server.wm.WindowManagerService.H.RESIZE_STACK;
import static com.android.server.wm.WindowManagerService.TAG;
import android.annotation.IntDef;
+import android.app.ActivityManager.StackId;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Debug;
@@ -122,12 +121,6 @@
}
}
- boolean allowTaskResize() {
- return mStackId == FREEFORM_WORKSPACE_STACK_ID
- || mStackId == DOCKED_STACK_ID
- || mStackId == PINNED_STACK_ID;
- }
-
/**
* Set the bounds of the stack and its containing tasks.
* @param stackBounds New stack bounds. Passing in null sets the bounds to fullscreen.
@@ -203,8 +196,7 @@
/** Return true if the current bound can get outputted to the rest of the system as-is. */
private boolean useCurrentBounds() {
if (mFullscreen
- || mStackId == DOCKED_STACK_ID
- || mStackId == PINNED_STACK_ID
+ || !StackId.isResizeableByDockedStack(mStackId)
|| mDisplayContent == null
|| mDisplayContent.getDockedStackLocked() != null) {
return true;
@@ -396,9 +388,9 @@
Rect bounds = null;
final TaskStack dockedStack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
- if (mStackId == DOCKED_STACK_ID || (dockedStack != null && mStackId != PINNED_STACK_ID
- && mStackId >= FIRST_STATIC_STACK_ID && mStackId <= LAST_STATIC_STACK_ID)) {
- // The existence of a docked stack affects the size of any static stack created since
+ if (mStackId == DOCKED_STACK_ID
+ || (dockedStack != null && StackId.isResizeableByDockedStack(mStackId))) {
+ // The existence of a docked stack affects the size of other static stack created since
// the docked stack occupies a dedicated region on screen.
bounds = new Rect();
displayContent.getLogicalDisplayRect(mTmpRect);
@@ -424,10 +416,7 @@
}
void getStackDockedModeBoundsLocked(Rect outBounds) {
- if (mStackId == DOCKED_STACK_ID
- || mStackId == PINNED_STACK_ID
- || mStackId > LAST_STATIC_STACK_ID
- || mDisplayContent == null) {
+ if (!StackId.isResizeableByDockedStack(mStackId) || mDisplayContent == null) {
outBounds.set(mBounds);
return;
}
@@ -537,9 +526,7 @@
for (int i = 0; i < count; i++) {
final TaskStack otherStack = mService.mStackIdToStack.valueAt(i);
final int otherStackId = otherStack.mStackId;
- if (otherStackId != DOCKED_STACK_ID && mStackId != PINNED_STACK_ID
- && otherStackId >= FIRST_STATIC_STACK_ID
- && otherStackId <= LAST_STATIC_STACK_ID) {
+ if (StackId.isResizeableByDockedStack(otherStackId)) {
mService.mH.sendMessage(
mService.mH.obtainMessage(RESIZE_STACK, otherStackId,
1 /*allowResizeInDockedMode*/, bounds));
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 7f2f2cd..4a9d8cb 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -16,7 +16,7 @@
package com.android.server.wm;
-import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 75680a0..230e81b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -17,8 +17,8 @@
package com.android.server.wm;
import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
-import static android.app.ActivityManager.DOCKED_STACK_ID;
-import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
@@ -2468,6 +2468,54 @@
}
}
+ void repositionChild(Session session, IWindow client,
+ int x, int y, long deferTransactionUntilFrame, Rect outFrame) {
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "repositionChild");
+ long origId = Binder.clearCallingIdentity();
+
+ try {
+ synchronized(mWindowMap) {
+ WindowState win = windowForClientLocked(session, client, false);
+ if (win == null) {
+ return;
+ }
+ if (win.mAttachedWindow == null) {
+ throw new IllegalArgumentException(
+ "repositionChild called but window is not"
+ + "attached to a parent win=" + win);
+ }
+
+ win.mFrame.left = x;
+ win.mFrame.top = y;
+
+ win.mWinAnimator.computeShownFrameLocked();
+
+ if (SHOW_TRANSACTIONS) {
+ Slog.i(TAG, ">>> OPEN TRANSACTION repositionChild");
+ }
+
+ SurfaceControl.openTransaction();
+
+ if (deferTransactionUntilFrame > 0) {
+ win.mWinAnimator.mSurfaceControl.deferTransactionUntil(
+ win.mAttachedWindow.mWinAnimator.mSurfaceControl.getHandle(),
+ deferTransactionUntilFrame);
+ }
+ win.mWinAnimator.setSurfaceBoundariesLocked(false);
+
+ SurfaceControl.closeTransaction();
+ if (SHOW_TRANSACTIONS) {
+ Slog.i(TAG, "<<< CLOSE TRANSACTION repositionChild");
+ }
+
+ outFrame = win.mCompatFrame;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+ }
+ }
+
public int relayoutWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int requestedWidth,
int requestedHeight, int viewVisibility, int flags,
@@ -4607,6 +4655,16 @@
}
}
+ @Override
+ public void cancelTaskWindowTransition(int taskId) {
+ synchronized (mWindowMap) {
+ Task task = mTaskIdToTask.get(taskId);
+ if (task != null) {
+ task.cancelTaskWindowTransition();
+ }
+ }
+ }
+
public void addTask(int taskId, int stackId, boolean toTop) {
synchronized (mWindowMap) {
if (DEBUG_STACK) Slog.i(TAG, "addTask: adding taskId=" + taskId
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 5bc329e..c1fa78a 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1999,4 +1999,13 @@
}
return mStringNameCache;
}
+
+ void transformFromScreenToSurfaceSpace(Rect rect) {
+ if (mHScale >= 0) {
+ rect.right = rect.left + (int)((rect.right - rect.left) / mHScale);
+ }
+ if (mVScale >= 0) {
+ rect.bottom = rect.top + (int)((rect.bottom - rect.top) / mVScale);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index aa242f1..8dddbd1 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1223,14 +1223,6 @@
mShownAlpha *= appTransformation.getAlpha();
if (appTransformation.hasClipRect()) {
mClipRect.set(appTransformation.getClipRect());
- if (mWin.mHScale > 0) {
- mClipRect.left /= mWin.mHScale;
- mClipRect.right /= mWin.mHScale;
- }
- if (mWin.mVScale > 0) {
- mClipRect.top /= mWin.mVScale;
- mClipRect.bottom /= mWin.mVScale;
- }
mHasClipRect = true;
}
}
@@ -1350,11 +1342,7 @@
final DisplayInfo displayInfo = displayContent.getDisplayInfo();
// Need to recompute a new system decor rect each time.
- if ((w.mAttrs.flags & LayoutParams.FLAG_SCALED) != 0) {
- // Currently can't do this cropping for scaled windows. We'll
- // just keep the crop rect the same as the source surface.
- w.mSystemDecorRect.set(0, 0, w.mRequestedWidth, w.mRequestedHeight);
- } else if (!w.isDefaultDisplay()) {
+ if (!w.isDefaultDisplay()) {
// On a different display there is no system decor. Crop the window
// by the screen boundaries.
w.mSystemDecorRect.set(0, 0, w.mCompatFrame.width(), w.mCompatFrame.height());
@@ -1407,9 +1395,13 @@
clipRect.offset(attrs.surfaceInsets.left, attrs.surfaceInsets.top);
// We don't want to clip to stack bounds windows that are currently doing entrance
// animation for docked window, otherwise the animating window will be suddenly cut off.
+
if (!(mAnimator.mAnimating && w.inDockedWorkspace())) {
adjustCropToStackBounds(w, clipRect);
}
+
+ w.transformFromScreenToSurfaceSpace(clipRect);
+
if (!clipRect.equals(mLastClipRect)) {
mLastClipRect.set(clipRect);
try {
diff --git a/services/core/jni/com_android_server_UsbMidiDevice.cpp b/services/core/jni/com_android_server_UsbMidiDevice.cpp
index 06b9bc3..e12a016 100644
--- a/services/core/jni/com_android_server_UsbMidiDevice.cpp
+++ b/services/core/jni/com_android_server_UsbMidiDevice.cpp
@@ -43,12 +43,26 @@
jint card, jint device)
{
char path[100];
+ int fd;
+ const int kMaxRetries = 10;
+ const int kSleepMicroseconds = 2000;
snprintf(path, sizeof(path), "/dev/snd/controlC%d", card);
- int fd = open(path, O_RDWR);
- if (fd < 0) {
- ALOGE("could not open %s", path);
- return 0;
+ // This control device may not have been created yet. So we should
+ // try to open it several times to prevent intermittent failure
+ // from a race condition.
+ int retryCounter = 0;
+ while ((fd = open(path, O_RDWR)) < 0) {
+ if (++retryCounter > kMaxRetries) {
+ ALOGE("timed out after %d tries, could not open %s", retryCounter, path);
+ return 0;
+ } else {
+ ALOGW("attempt #%d, could not open %s", retryCounter, path);
+ // Increase the sleep interval each time.
+ // 10 retries will total 2 * sum(1..10) = 110 milliseconds.
+ // Typically the device should be ready in 5-10 milliseconds.
+ usleep(kSleepMicroseconds * retryCounter);
+ }
}
struct snd_rawmidi_info info;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index b4c8f96..1602c12 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1016,6 +1016,7 @@
}
}
+ // DO NOT call it while taking the "this" lock, which could cause a dead lock.
private void handlePackagesChanged(String packageName, int userHandle) {
boolean removed = false;
if (VERBOSE_LOG) Slog.d(LOG_TAG, "Handling package changes for user " + userHandle);
@@ -1042,7 +1043,6 @@
}
if (removed) {
validatePasswordOwnerLocked(policy);
- syncDeviceCapabilitiesLocked(policy);
saveSettingsLocked(policy.mUserHandle);
}
@@ -1061,6 +1061,14 @@
}
}
}
+ if (removed) {
+ synchronized (mUserManagerInternal.getUserRestrictionsLock()) {
+ synchronized (DevicePolicyManagerService.this) {
+ mUserManagerInternal.updateEffectiveUserRestrictionsRL(
+ userHandle);
+ }
+ }
+ }
}
/**
@@ -1682,7 +1690,7 @@
}
}
- void removeActiveAdminLocked(final ComponentName adminReceiver, int userHandle) {
+ void removeActiveAdminLocked(final ComponentName adminReceiver, final int userHandle) {
final ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver, userHandle);
if (admin != null) {
synchronized (this) {
@@ -1701,7 +1709,6 @@
policy.mAdminList.remove(admin);
policy.mAdminMap.remove(adminReceiver);
validatePasswordOwnerLocked(policy);
- syncDeviceCapabilitiesLocked(policy);
if (doProxyCleanup) {
resetGlobalProxyLocked(getUserData(userHandle));
}
@@ -1709,6 +1716,12 @@
updateMaximumTimeToLockLocked(policy);
policy.mRemovingAdmins.remove(adminReceiver);
}
+ synchronized (mUserManagerInternal.getUserRestrictionsLock()) {
+ synchronized (DevicePolicyManagerService.this) {
+ mUserManagerInternal.updateEffectiveUserRestrictionsRL(
+ userHandle);
+ }
+ }
}
});
}
@@ -2022,7 +2035,6 @@
}
validatePasswordOwnerLocked(policy);
- syncDeviceCapabilitiesLocked(policy);
updateMaximumTimeToLockLocked(policy);
updateLockTaskPackagesLocked(policy.mLockTaskPackages, userHandle);
if (policy.mStatusBarDisabled) {
@@ -2089,31 +2101,6 @@
}
}
- /**
- * Pushes down policy information to the system for any policies related to general device
- * capabilities that need to be enforced by lower level services (e.g. Camera services).
- */
- void syncDeviceCapabilitiesLocked(DevicePolicyData policy) {
- // Ensure the status of the camera is synced down to the system. Interested native services
- // should monitor this value and act accordingly.
- String cameraPropertyForUser = SYSTEM_PROP_DISABLE_CAMERA_PREFIX + policy.mUserHandle;
- boolean systemState = mInjector.systemPropertiesGetBoolean(cameraPropertyForUser, false);
- boolean cameraDisabled = getCameraDisabled(null, policy.mUserHandle);
- if (cameraDisabled != systemState) {
- long token = mInjector.binderClearCallingIdentity();
- try {
- String value = cameraDisabled ? "1" : "0";
- if (VERBOSE_LOG) {
- Slog.v(LOG_TAG, "Change in camera state ["
- + cameraPropertyForUser + "] = " + value);
- }
- mInjector.systemPropertiesSet(cameraPropertyForUser, value);
- } finally {
- mInjector.binderRestoreCallingIdentity(token);
- }
- }
- }
-
@VisibleForTesting
void systemReady(int phase) {
if (!mHasFeature) {
@@ -4329,13 +4316,6 @@
}
/**
- * The system property used to share the state of the camera. The native camera service
- * is expected to read this property and act accordingly. The userId should be appended
- * to this key.
- */
- public static final String SYSTEM_PROP_DISABLE_CAMERA_PREFIX = "sys.secpolicy.camera.off_";
-
- /**
* Disables all device cameras according to the specified admin.
*/
@Override
@@ -4352,7 +4332,16 @@
ap.disableCamera = disabled;
saveSettingsLocked(userHandle);
}
- syncDeviceCapabilitiesLocked(getUserData(userHandle));
+ }
+ // Tell the user manager that the restrictions have changed.
+ synchronized (mUserManagerInternal.getUserRestrictionsLock()) {
+ synchronized (this) {
+ if (isDeviceOwner(who)) {
+ mUserManagerInternal.updateEffectiveUserRestrictionsForAllUsersRL();
+ } else {
+ mUserManagerInternal.updateEffectiveUserRestrictionsRL(userHandle);
+ }
+ }
}
}
@@ -4370,7 +4359,13 @@
ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
return (admin != null) ? admin.disableCamera : false;
}
+ // First, see if DO has set it. If so, it's device-wide.
+ final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
+ if (deviceOwner != null && deviceOwner.disableCamera) {
+ return true;
+ }
+ // Then check each device admin on the user.
DevicePolicyData policy = getUserData(userHandle);
// Determine whether or not the device camera is disabled for any active admins.
final int N = policy.mAdminList.size();
@@ -4404,7 +4399,6 @@
ap.disabledKeyguardFeatures = which;
saveSettingsLocked(userHandle);
}
- syncDeviceCapabilitiesLocked(getUserData(userHandle));
}
}
@@ -5036,7 +5030,6 @@
DeviceAdminInfo.USES_POLICY_DISABLE_KEYGUARD_FEATURES);
ap.trustAgentInfos.put(agent.flattenToString(), new TrustAgentInfo(args));
saveSettingsLocked(userHandle);
- syncDeviceCapabilitiesLocked(getUserData(userHandle));
}
}
@@ -5602,6 +5595,7 @@
}
}
+ // DO NOT call it while taking the "this" lock, which could cause a dead lock.
@Override
public void setUserRestriction(ComponentName who, String key, boolean enabledFromThisOwner) {
Preconditions.checkNotNull(who, "ComponentName is null");
@@ -5612,7 +5606,7 @@
ActiveAdmin activeAdmin =
getActiveAdminForCallerLocked(who,
DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- boolean isDeviceOwner = isDeviceOwner(who);
+ final boolean isDeviceOwner = isDeviceOwner(who);
if (!isDeviceOwner && userHandle != UserHandle.USER_SYSTEM
&& DEVICE_OWNER_USER_RESTRICTIONS.contains(key)) {
throw new SecurityException(
@@ -5624,9 +5618,6 @@
final long id = mInjector.binderClearCallingIdentity();
try {
- // Original value.
- final boolean alreadyRestricted = mUserManager.hasUserRestriction(key, user);
-
// Save the restriction to ActiveAdmin.
// TODO When DO sets a restriction, it'll always be treated as device-wide.
// If there'll be a policy that can be set by both, we'll need scoping support,
@@ -5635,85 +5626,12 @@
activeAdmin.ensureUserRestrictions().putBoolean(key, enabledFromThisOwner);
saveSettingsLocked(userHandle);
- // Tell UserManager the new value. Note this needs to be done before calling
- // into AudioService, because AS will check AppOps that'll be updated by UM.
+ // Tell UserManager the new value.
if (isDeviceOwner) {
mUserManagerInternal.updateEffectiveUserRestrictionsForAllUsersRL();
} else {
mUserManagerInternal.updateEffectiveUserRestrictionsRL(userHandle);
}
-
- // New value.
- final boolean enabled = mUserManager.hasUserRestriction(key, user);
-
- // TODO The rest of the code should move to UserManagerService.
-
- if (enabled && !alreadyRestricted) {
- if (UserManager.DISALLOW_UNMUTE_MICROPHONE.equals(key)) {
- mInjector.getIAudioService()
- .setMicrophoneMute(true, mContext.getPackageName(), userHandle);
- } else if (UserManager.DISALLOW_ADJUST_VOLUME.equals(key)) {
- mInjector.getIAudioService()
- .setMasterMute(true, 0, mContext.getPackageName(), userHandle);
- } else if (UserManager.DISALLOW_CONFIG_WIFI.equals(key)) {
- mInjector.settingsSecurePutIntForUser(
- Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 0,
- userHandle);
- } else if (UserManager.DISALLOW_SHARE_LOCATION.equals(key)) {
- mInjector.settingsSecurePutIntForUser(
- Settings.Secure.LOCATION_MODE,
- Settings.Secure.LOCATION_MODE_OFF,
- userHandle);
- mInjector.settingsSecurePutStringForUser(
- Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "",
- userHandle);
- } else if (UserManager.DISALLOW_DEBUGGING_FEATURES.equals(key)) {
- // Only disable adb if changing for system user, since it is global
- // TODO: should this be admin user?
- if (userHandle == UserHandle.USER_SYSTEM) {
- mInjector.settingsGlobalPutStringForUser(
- Settings.Global.ADB_ENABLED, "0", userHandle);
- }
- } else if (UserManager.ENSURE_VERIFY_APPS.equals(key)) {
- mInjector.settingsGlobalPutStringForUser(
- Settings.Global.PACKAGE_VERIFIER_ENABLE, "1",
- userHandle);
- mInjector.settingsGlobalPutStringForUser(
- Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, "1",
- userHandle);
- } else if (UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES.equals(key)) {
- mInjector.settingsSecurePutIntForUser(
- Settings.Secure.INSTALL_NON_MARKET_APPS, 0,
- userHandle);
- }
- }
-
- if (enabled != alreadyRestricted) {
- if (UserManager.DISALLOW_SHARE_LOCATION.equals(key)) {
- // Send out notifications however as some clients may want to reread the
- // value which actually changed due to a restriction having been
- // applied.
- final String property = Settings.Secure.SYS_PROP_SETTING_VERSION;
- long version = mInjector.systemPropertiesGetLong(property, 0) + 1;
- mInjector.systemPropertiesSet(property, Long.toString(version));
-
- final String name = Settings.Secure.LOCATION_PROVIDERS_ALLOWED;
- Uri url = Uri.withAppendedPath(Settings.Secure.CONTENT_URI, name);
- mContext.getContentResolver().notifyChange(url, null, true, userHandle);
- }
- }
- if (!enabled && alreadyRestricted) {
- if (UserManager.DISALLOW_UNMUTE_MICROPHONE.equals(key)) {
- mInjector.getIAudioService()
- .setMicrophoneMute(false, mContext.getPackageName(),
- userHandle);
- } else if (UserManager.DISALLOW_ADJUST_VOLUME.equals(key)) {
- mInjector.getIAudioService()
- .setMasterMute(false, 0, mContext.getPackageName(), userHandle);
- }
- }
- } catch (RemoteException re) {
- Slog.e(LOG_TAG, "Failed to talk to AudioService.", re);
} finally {
mInjector.binderRestoreCallingIdentity(id);
}
@@ -6463,8 +6381,10 @@
deviceOwner == null ? null : deviceOwner.userRestrictions;
final Bundle profileOwnerRestrictions =
profileOwner == null ? null : profileOwner.userRestrictions;
+ final boolean cameraDisabled = getCameraDisabled(null, userId);
- if (deviceOwnerRestrictions == null && profileOwnerRestrictions == null) {
+ if (deviceOwnerRestrictions == null && profileOwnerRestrictions == null
+ && !cameraDisabled) {
// No restrictions to merge.
return inBundle;
}
@@ -6473,6 +6393,11 @@
UserRestrictionsUtils.merge(composed, deviceOwnerRestrictions);
UserRestrictionsUtils.merge(composed, profileOwnerRestrictions);
+ // Also merge in the camera restriction.
+ if (cameraDisabled) {
+ composed.putBoolean(UserManager.DISALLOW_CAMERA, true);
+ }
+
return composed;
}
}
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index e11c8d3..d1d6e0d 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -332,9 +332,24 @@
return 0;
}
+ /**
+ * @hide
+ */
+ public static String givePrintableIccid(String iccId) {
+ String iccIdToPrint = null;
+ if (iccId != null) {
+ if (iccId.length() > 9) {
+ iccIdToPrint = iccId.substring(0, 9) + "XXXXXXXXXXX";
+ } else {
+ iccIdToPrint = iccId;
+ }
+ }
+ return iccIdToPrint;
+ }
+
@Override
public String toString() {
- String iccIdToPrint = mIccId != null ? mIccId.substring(0, 9) + "XXXXXXXXXXX" : null;
+ String iccIdToPrint = givePrintableIccid(mIccId);
return "{id=" + mId + ", iccId=" + iccIdToPrint + " simSlotIndex=" + mSimSlotIndex
+ " displayName=" + mDisplayName + " carrierName=" + mCarrierName
+ " nameSource=" + mNameSource + " iconTint=" + mIconTint
diff --git a/telephony/java/com/android/ims/ImsCallProfile.java b/telephony/java/com/android/ims/ImsCallProfile.java
index 861a379..5f84e0c 100644
--- a/telephony/java/com/android/ims/ImsCallProfile.java
+++ b/telephony/java/com/android/ims/ImsCallProfile.java
@@ -188,6 +188,20 @@
public static final String EXTRA_CODEC = "Codec";
public static final String EXTRA_DISPLAY_TEXT = "DisplayText";
public static final String EXTRA_ADDITIONAL_CALL_INFO = "AdditionalCallInfo";
+
+ /**
+ * Extra key which the RIL can use to indicate the radio technology used for a call.
+ * Valid values are:
+ * {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE},
+ * {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_IWLAN}, and the other defined
+ * {@code RIL_RADIO_TECHNOLOGY_*} constants.
+ * Note: Despite the fact the {@link android.telephony.ServiceState} values are integer
+ * constants, the values passed for the {@link #EXTRA_CALL_RAT_TYPE} should be strings (e.g.
+ * "14" vs (int) 14).
+ * Note: This is used by {@link com.android.internal.telephony.imsphone.ImsPhoneConnection#
+ * updateWifiStateFromExtras(Bundle)} to determine whether to set the
+ * {@link android.telecom.Connection#CAPABILITY_WIFI} capability on a connection.
+ */
public static final String EXTRA_CALL_RAT_TYPE = "CallRadioTech";
public int mServiceType;
diff --git a/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionMeasuringActivity.java b/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionMeasuringActivity.java
index b4e0c70..4771b6c 100644
--- a/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionMeasuringActivity.java
+++ b/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionMeasuringActivity.java
@@ -24,6 +24,7 @@
import android.app.ActivityManager;
import android.app.ActivityManager.MemoryInfo;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.Rect;
@@ -95,11 +96,9 @@
private boolean mResumed;
// Drop one frame per half second.
- // TODO(khmel)
- // Add a feature flag and set the target FPS dependent on the target system as e.g.:
- // 59FPS for MULTI_WINDOW and 54 otherwise (to satisfy the default lax Android requirements).
private double mRefreshRate;
private double mTargetFPS;
+ private boolean mAndromeda;
private int mWidth;
private int mHeight;
@@ -182,6 +181,10 @@
return score;
}
+ public boolean isAndromeda() {
+ return mAndromeda;
+ }
+
@Override
public void onClick(View view) {
if (view == mMeasureCompositionButton) {
@@ -247,6 +250,9 @@
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ // Detect Andromeda devices by having free-form window management feature.
+ mAndromeda = getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT);
detectRefreshRate();
// To layouts in parent. First contains list of Surfaces and second
@@ -513,7 +519,8 @@
}
MemoryInfo memInfo = getMemoryInfo();
- String info = "Available " +
+ String platformName = mAndromeda ? "Andromeda" : "Android";
+ String info = platformName + ": available " +
getReadableMemory(memInfo.availMem) + " from " +
getReadableMemory(memInfo.totalMem) + ".\nVisible " +
visibleCnt + " from " + mViews.size() + " " +
diff --git a/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionTest.java b/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionTest.java
index 3f04888..388f91a 100644
--- a/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionTest.java
+++ b/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionTest.java
@@ -17,6 +17,7 @@
import android.app.Activity;
import android.graphics.PixelFormat;
+import android.os.Build;
import android.os.Bundle;
import android.surfacecomposition.SurfaceCompositionMeasuringActivity.AllocationScore;
import android.surfacecomposition.SurfaceCompositionMeasuringActivity.CompositorScore;
@@ -44,11 +45,16 @@
PixelFormat.OPAQUE,
};
- // Based on Nexus 9 performance which is usually < 9.0.
- private final static double[] MIN_ACCEPTED_COMPOSITION_SCORE = new double[] {
+ // Nexus 9 performance is around 8.8. We distinguish results for Andromeda and
+ // Android devices. Andromeda devices require higher performance score.
+ private final static double[] MIN_ACCEPTED_COMPOSITION_SCORE_ANDROMDEDA = new double[] {
8.0,
8.0,
};
+ private final static double[] MIN_ACCEPTED_COMPOSITION_SCORE_ANDROID = new double[] {
+ 4.0,
+ 4.0,
+ };
// Based on Nexus 6 performance which is usually < 28.0.
private final static double[] MIN_ACCEPTED_ALLOCATION_SCORE = new double[] {
@@ -66,6 +72,8 @@
@SmallTest
public void testSurfaceCompositionPerformance() {
Bundle status = new Bundle();
+ double[] minScores = getActivity().isAndromeda() ?
+ MIN_ACCEPTED_COMPOSITION_SCORE_ANDROMDEDA : MIN_ACCEPTED_COMPOSITION_SCORE_ANDROID;
for (int i = 0; i < TEST_PIXEL_FORMATS.length; ++i) {
int pixelFormat = TEST_PIXEL_FORMATS[i];
String formatName = SurfaceCompositionMeasuringActivity.getPixelFormatInfo(pixelFormat);
@@ -73,8 +81,8 @@
Log.i(TAG, "testSurfaceCompositionPerformance(" + formatName + ") = " + score);
assertTrue("Device does not support surface(" + formatName + ") composition " +
"performance score. " + score.mSurfaces + " < " +
- MIN_ACCEPTED_COMPOSITION_SCORE[i] + ".",
- score.mSurfaces >= MIN_ACCEPTED_COMPOSITION_SCORE[i]);
+ minScores[i] + ". Build: " + Build.FINGERPRINT + ".",
+ score.mSurfaces >= minScores[i]);
// Send status only for TRANSLUCENT format.
if (pixelFormat == PixelFormat.TRANSLUCENT) {
status.putDouble(KEY_SURFACE_COMPOSITION_PERFORMANCE, score.mSurfaces);
@@ -96,7 +104,8 @@
Log.i(TAG, "testSurfaceAllocationPerformance(" + formatName + ") = " + score);
assertTrue("Device does not support surface(" + formatName + ") allocation " +
"performance score. " + score.mMedian + " < " +
- MIN_ACCEPTED_ALLOCATION_SCORE[i] + ".",
+ MIN_ACCEPTED_ALLOCATION_SCORE[i] + ". Build: " +
+ Build.FINGERPRINT + ".",
score.mMedian >= MIN_ACCEPTED_ALLOCATION_SCORE[i]);
// Send status only for TRANSLUCENT format.
if (pixelFormat == PixelFormat.TRANSLUCENT) {
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk
index 275476c..a41d2d7 100644
--- a/tools/aapt2/Android.mk
+++ b/tools/aapt2/Android.mk
@@ -45,9 +45,11 @@
ConfigDescription.cpp \
Debug.cpp \
Flags.cpp \
- JavaClassGenerator.cpp \
+ java/AnnotationProcessor.cpp \
+ java/JavaClassGenerator.cpp \
+ java/ManifestClassGenerator.cpp \
+ java/ProguardRules.cpp \
Locale.cpp \
- ProguardRules.cpp \
Resource.cpp \
ResourceParser.cpp \
ResourceTable.cpp \
@@ -76,7 +78,8 @@
util/StringPiece_test.cpp \
util/Util_test.cpp \
ConfigDescription_test.cpp \
- JavaClassGenerator_test.cpp \
+ java/JavaClassGenerator_test.cpp \
+ java/ManifestClassGenerator_test.cpp \
Locale_test.cpp \
Resource_test.cpp \
ResourceParser_test.cpp \
diff --git a/tools/aapt2/Resource.cpp b/tools/aapt2/Resource.cpp
index 1962f58..34dc1d5 100644
--- a/tools/aapt2/Resource.cpp
+++ b/tools/aapt2/Resource.cpp
@@ -36,7 +36,6 @@
case ResourceType::kFraction: return u"fraction";
case ResourceType::kId: return u"id";
case ResourceType::kInteger: return u"integer";
- case ResourceType::kIntegerArray: return u"integer-array";
case ResourceType::kInterpolator: return u"interpolator";
case ResourceType::kLayout: return u"layout";
case ResourceType::kMenu: return u"menu";
@@ -65,7 +64,6 @@
{ u"fraction", ResourceType::kFraction },
{ u"id", ResourceType::kId },
{ u"integer", ResourceType::kInteger },
- { u"integer-array", ResourceType::kIntegerArray },
{ u"interpolator", ResourceType::kInterpolator },
{ u"layout", ResourceType::kLayout },
{ u"menu", ResourceType::kMenu },
diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h
index 7ef1897..a7afbb5 100644
--- a/tools/aapt2/Resource.h
+++ b/tools/aapt2/Resource.h
@@ -47,7 +47,6 @@
kFraction,
kId,
kInteger,
- kIntegerArray,
kInterpolator,
kLayout,
kMenu,
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 44710eb..0c7a4d5 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -308,6 +308,9 @@
} else if (elementName == u"dimen") {
parsedResource.name.type = ResourceType::kDimen;
result = parsePrimitive(parser, &parsedResource);
+ } else if (elementName == u"fraction") {
+ parsedResource.name.type = ResourceType::kFraction;
+ result = parsePrimitive(parser, &parsedResource);
} else if (elementName == u"style") {
parsedResource.name.type = ResourceType::kStyle;
result = parseStyle(parser, &parsedResource);
@@ -321,7 +324,7 @@
parsedResource.name.type = ResourceType::kArray;
result = parseArray(parser, &parsedResource, android::ResTable_map::TYPE_STRING);
} else if (elementName == u"integer-array") {
- parsedResource.name.type = ResourceType::kIntegerArray;
+ parsedResource.name.type = ResourceType::kArray;
result = parseArray(parser, &parsedResource, android::ResTable_map::TYPE_INTEGER);
} else if (elementName == u"declare-styleable") {
parsedResource.name.type = ResourceType::kStyleable;
@@ -464,6 +467,8 @@
typeMask |= android::ResTable_map::TYPE_INTEGER;
break;
+ case ResourceType::kFraction:
+ // fallthrough
case ResourceType::kDimen:
typeMask |= android::ResTable_map::TYPE_DIMENSION
| android::ResTable_map::TYPE_FLOAT
@@ -576,6 +581,12 @@
return mask;
}
+/**
+ * Returns true if the element is <skip> or <eat-comment> and can be safely ignored.
+ */
+static bool shouldIgnoreElement(const StringPiece16& ns, const StringPiece16& name) {
+ return ns.empty() && (name == u"skip" || name == u"eat-comment");
+}
bool ResourceParser::parseAttr(XmlPullParser* parser, ParsedResource* outResource) {
outResource->source = mSource.withLine(parser->getLineNumber());
@@ -613,25 +624,30 @@
bool error = false;
const size_t depth = parser->getDepth();
while (XmlPullParser::nextChildNode(parser, depth)) {
- if (parser->getEvent() != XmlPullParser::Event::kStartElement) {
- // Skip comments and text.
+ if (parser->getEvent() == XmlPullParser::Event::kComment) {
+ comment = util::trimWhitespace(parser->getComment()).toString();
+ continue;
+ } else if (parser->getEvent() != XmlPullParser::Event::kStartElement) {
+ // Skip text.
continue;
}
+ const Source itemSource = mSource.withLine(parser->getLineNumber());
const std::u16string& elementNamespace = parser->getElementNamespace();
const std::u16string& elementName = parser->getElementName();
- if (elementNamespace == u"" && (elementName == u"flag" || elementName == u"enum")) {
+ if (elementNamespace.empty() && (elementName == u"flag" || elementName == u"enum")) {
if (elementName == u"enum") {
if (typeMask & android::ResTable_map::TYPE_FLAGS) {
- mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
+ mDiag->error(DiagMessage(itemSource)
<< "can not define an <enum>; already defined a <flag>");
error = true;
continue;
}
typeMask |= android::ResTable_map::TYPE_ENUM;
+
} else if (elementName == u"flag") {
if (typeMask & android::ResTable_map::TYPE_ENUM) {
- mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
+ mDiag->error(DiagMessage(itemSource)
<< "can not define a <flag>; already defined an <enum>");
error = true;
continue;
@@ -642,21 +658,22 @@
if (Maybe<Attribute::Symbol> s = parseEnumOrFlagItem(parser, elementName)) {
ParsedResource childResource;
childResource.name = s.value().symbol.name.value();
- childResource.source = mSource.withLine(parser->getLineNumber());
+ childResource.source = itemSource;
childResource.value = util::make_unique<Id>();
outResource->childResources.push_back(std::move(childResource));
+
+ s.value().symbol.setComment(std::move(comment));
+ s.value().symbol.setSource(itemSource);
items.push_back(std::move(s.value()));
} else {
error = true;
}
- } else if (elementName == u"skip" || elementName == u"eat-comment") {
- comment = u"";
-
- } else {
- mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
- << ":" << elementName << ">");
+ } else if (!shouldIgnoreElement(elementNamespace, elementName)) {
+ mDiag->error(DiagMessage(itemSource) << ":" << elementName << ">");
error = true;
}
+
+ comment = {};
}
if (error) {
@@ -716,11 +733,10 @@
p++;
}
- return ResourceName{ package.toString(), ResourceType::kAttr,
- name.empty() ? str.toString() : name.toString() };
+ return ResourceName(package.toString(), ResourceType::kAttr,
+ name.empty() ? str.toString() : name.toString());
}
-
bool ResourceParser::parseStyleItem(XmlPullParser* parser, Style* style) {
const Source source = mSource.withLine(parser->getLineNumber());
@@ -783,7 +799,6 @@
}
bool error = false;
- std::u16string comment;
const size_t depth = parser->getDepth();
while (XmlPullParser::nextChildNode(parser, depth)) {
if (parser->getEvent() != XmlPullParser::Event::kStartElement) {
@@ -796,11 +811,7 @@
if (elementNamespace == u"" && elementName == u"item") {
error |= !parseStyleItem(parser, style.get());
- } else if (elementNamespace.empty() &&
- (elementName == u"skip" || elementName == u"eat-comment")) {
- comment = u"";
-
- } else {
+ } else if (!shouldIgnoreElement(elementNamespace, elementName)) {
mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
<< ":" << elementName << ">");
error = true;
@@ -820,7 +831,6 @@
const Source source = mSource.withLine(parser->getLineNumber());
std::unique_ptr<Array> array = util::make_unique<Array>();
- std::u16string comment;
bool error = false;
const size_t depth = parser->getDepth();
while (XmlPullParser::nextChildNode(parser, depth)) {
@@ -839,13 +849,10 @@
error = true;
continue;
}
+ item->setSource(itemSource);
array->items.emplace_back(std::move(item));
- } else if (elementNamespace.empty() &&
- (elementName == u"skip" || elementName == u"eat-comment")) {
- comment = u"";
-
- } else {
+ } else if (!shouldIgnoreElement(elementNamespace, elementName)) {
mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
<< "unknown tag <" << elementNamespace << ":" << elementName << ">");
error = true;
@@ -864,7 +871,6 @@
const Source source = mSource.withLine(parser->getLineNumber());
std::unique_ptr<Plural> plural = util::make_unique<Plural>();
- std::u16string comment;
bool error = false;
const size_t depth = parser->getDepth();
while (XmlPullParser::nextChildNode(parser, depth)) {
@@ -873,13 +879,14 @@
continue;
}
+ const Source itemSource = mSource.withLine(parser->getLineNumber());
const std::u16string& elementNamespace = parser->getElementNamespace();
const std::u16string& elementName = parser->getElementName();
if (elementNamespace.empty() && elementName == u"item") {
const auto endAttrIter = parser->endAttributes();
auto attrIter = parser->findAttribute(u"", u"quantity");
if (attrIter == endAttrIter || attrIter->value.empty()) {
- mDiag->error(DiagMessage(source) << "<item> in <plurals> requires attribute "
+ mDiag->error(DiagMessage(itemSource) << "<item> in <plurals> requires attribute "
<< "'quantity'");
error = true;
continue;
@@ -900,7 +907,7 @@
} else if (trimmedQuantity == u"other") {
index = Plural::Other;
} else {
- mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
+ mDiag->error(DiagMessage(itemSource)
<< "<item> in <plural> has invalid value '" << trimmedQuantity
<< "' for attribute 'quantity'");
error = true;
@@ -908,7 +915,7 @@
}
if (plural->values[index]) {
- mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
+ mDiag->error(DiagMessage(itemSource)
<< "duplicate quantity '" << trimmedQuantity << "'");
error = true;
continue;
@@ -918,11 +925,10 @@
kNoRawString))) {
error = true;
}
- } else if (elementNamespace.empty() &&
- (elementName == u"skip" || elementName == u"eat-comment")) {
- comment = u"";
- } else {
- mDiag->error(DiagMessage(source) << "unknown tag <" << elementNamespace << ":"
+ plural->values[index]->setSource(itemSource);
+
+ } else if (!shouldIgnoreElement(elementNamespace, elementName)) {
+ mDiag->error(DiagMessage(itemSource) << "unknown tag <" << elementNamespace << ":"
<< elementName << ">");
error = true;
}
@@ -944,43 +950,52 @@
bool error = false;
const size_t depth = parser->getDepth();
while (XmlPullParser::nextChildNode(parser, depth)) {
- if (parser->getEvent() != XmlPullParser::Event::kStartElement) {
- // Ignore text and comments.
+ if (parser->getEvent() == XmlPullParser::Event::kComment) {
+ comment = util::trimWhitespace(parser->getComment()).toString();
+ continue;
+ } else if (parser->getEvent() != XmlPullParser::Event::kStartElement) {
+ // Ignore text.
continue;
}
+ const Source itemSource = mSource.withLine(parser->getLineNumber());
const std::u16string& elementNamespace = parser->getElementNamespace();
const std::u16string& elementName = parser->getElementName();
if (elementNamespace.empty() && elementName == u"attr") {
const auto endAttrIter = parser->endAttributes();
auto attrIter = parser->findAttribute(u"", u"name");
if (attrIter == endAttrIter || attrIter->value.empty()) {
- mDiag->error(DiagMessage(source) << "<attr> tag must have a 'name' attribute");
+ mDiag->error(DiagMessage(itemSource) << "<attr> tag must have a 'name' attribute");
error = true;
continue;
}
+ // Create the ParsedResource that will add the attribute to the table.
ParsedResource childResource;
childResource.name = ResourceName({}, ResourceType::kAttr, attrIter->value);
- childResource.source = mSource.withLine(parser->getLineNumber());
+ childResource.source = itemSource;
+ childResource.comment = std::move(comment);
if (!parseAttrImpl(parser, &childResource, true)) {
error = true;
continue;
}
- styleable->entries.push_back(Reference(childResource.name));
+ // Create the reference to this attribute.
+ Reference childRef(childResource.name);
+ childRef.setComment(childResource.comment);
+ childRef.setSource(itemSource);
+ styleable->entries.push_back(std::move(childRef));
+
outResource->childResources.push_back(std::move(childResource));
- } else if (elementNamespace.empty() &&
- (elementName == u"skip" || elementName == u"eat-comment")) {
- comment = u"";
-
- } else {
- mDiag->error(DiagMessage(source) << "unknown tag <" << elementNamespace << ":"
+ } else if (!shouldIgnoreElement(elementNamespace, elementName)) {
+ mDiag->error(DiagMessage(itemSource) << "unknown tag <" << elementNamespace << ":"
<< elementName << ">");
error = true;
}
+
+ comment = {};
}
if (error) {
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index af6bf67..2f5daae 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -414,6 +414,34 @@
EXPECT_EQ(value->getComment(), u"One");
}
+TEST_F(ResourceParserTest, ParseNestedComments) {
+ // We only care about declare-styleable and enum/flag attributes because comments
+ // from those end up in R.java
+ std::string input = R"EOF(
+ <declare-styleable name="foo">
+ <!-- The name of the bar -->
+ <attr name="barName" format="string|reference" />
+ </declare-styleable>
+
+ <attr name="foo">
+ <!-- The very first -->
+ <enum name="one" value="1" />
+ </attr>)EOF";
+ ASSERT_TRUE(testParse(input));
+
+ Styleable* styleable = test::getValue<Styleable>(&mTable, u"@styleable/foo");
+ ASSERT_NE(nullptr, styleable);
+ ASSERT_EQ(1u, styleable->entries.size());
+
+ EXPECT_EQ(StringPiece16(u"The name of the bar"), styleable->entries.front().getComment());
+
+ Attribute* attr = test::getValue<Attribute>(&mTable, u"@attr/foo");
+ ASSERT_NE(nullptr, attr);
+ ASSERT_EQ(1u, attr->symbols.size());
+
+ EXPECT_EQ(StringPiece16(u"The very first"), attr->symbols.front().symbol.getComment());
+}
+
/*
* Declaring an ID as public should not require a separate definition
* (as an ID has no value).
diff --git a/tools/aapt2/Resource_test.cpp b/tools/aapt2/Resource_test.cpp
index d957999..48dc521 100644
--- a/tools/aapt2/Resource_test.cpp
+++ b/tools/aapt2/Resource_test.cpp
@@ -69,10 +69,6 @@
ASSERT_NE(type, nullptr);
EXPECT_EQ(*type, ResourceType::kInteger);
- type = parseResourceType(u"integer-array");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kIntegerArray);
-
type = parseResourceType(u"interpolator");
ASSERT_NE(type, nullptr);
EXPECT_EQ(*type, ResourceType::kInterpolator);
diff --git a/tools/aapt2/XmlDom.cpp b/tools/aapt2/XmlDom.cpp
index d948775..b769c76 100644
--- a/tools/aapt2/XmlDom.cpp
+++ b/tools/aapt2/XmlDom.cpp
@@ -125,7 +125,7 @@
Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
assert(!stack->nodeStack.empty());
- stack->nodeStack.top()->comment = std::move(stack->pendingComment);
+ //stack->nodeStack.top()->comment = std::move(stack->pendingComment);
stack->nodeStack.pop();
}
@@ -194,7 +194,7 @@
XML_ParserFree(parser);
if (stack.root) {
- return util::make_unique<XmlResource>(ResourceFile{}, std::move(stack.root));
+ return util::make_unique<XmlResource>(ResourceFile{ {}, {}, source }, std::move(stack.root));
}
return {};
}
@@ -317,6 +317,22 @@
return util::make_unique<XmlResource>(ResourceFile{}, std::move(root));
}
+Element* findRootElement(Node* node) {
+ if (!node) {
+ return nullptr;
+ }
+
+ Element* el = nullptr;
+ while ((el = nodeCast<Element>(node)) == nullptr) {
+ if (node->children.empty()) {
+ return nullptr;
+ }
+ // We are looking for the first element, and namespaces can only have one child.
+ node = node->children.front().get();
+ }
+ return el;
+}
+
void Node::addChild(std::unique_ptr<Node> child) {
child->parent = this;
children.push_back(std::move(child));
diff --git a/tools/aapt2/XmlDom.h b/tools/aapt2/XmlDom.h
index c095f08..b1987f1 100644
--- a/tools/aapt2/XmlDom.h
+++ b/tools/aapt2/XmlDom.h
@@ -132,6 +132,8 @@
std::unique_ptr<XmlResource> inflate(const void* data, size_t dataLen, IDiagnostics* diag,
const Source& source);
+Element* findRootElement(Node* node);
+
/**
* A visitor interface for the different XML Node subtypes. This will not traverse into
* children. Use Visitor for that.
diff --git a/tools/aapt2/java/AnnotationProcessor.cpp b/tools/aapt2/java/AnnotationProcessor.cpp
new file mode 100644
index 0000000..16440bc
--- /dev/null
+++ b/tools/aapt2/java/AnnotationProcessor.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2015 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 "java/AnnotationProcessor.h"
+#include "util/Util.h"
+
+#include <algorithm>
+
+namespace aapt {
+
+void AnnotationProcessor::appendCommentLine(const StringPiece16& line) {
+ static const std::string sDeprecated = "@deprecated";
+ static const std::string sSystemApi = "@SystemApi";
+
+ if (line.empty()) {
+ return;
+ }
+
+ std::string comment = util::utf16ToUtf8(line);
+
+ if (comment.find(sDeprecated) != std::string::npos && !mDeprecated) {
+ mDeprecated = true;
+ if (!mAnnotations.empty()) {
+ mAnnotations += "\n";
+ }
+ mAnnotations += mPrefix;
+ mAnnotations += "@Deprecated";
+ }
+
+ if (comment.find(sSystemApi) != std::string::npos && !mSystemApi) {
+ mSystemApi = true;
+ if (!mAnnotations.empty()) {
+ mAnnotations += "\n";
+ }
+ mAnnotations += mPrefix;
+ mAnnotations += "@android.annotations.SystemApi";
+ }
+
+ if (mComment.empty()) {
+ mComment += mPrefix;
+ mComment += "/**";
+ }
+
+ mComment += "\n";
+ mComment += mPrefix;
+ mComment += " * ";
+ mComment += std::move(comment);
+}
+
+void AnnotationProcessor::appendComment(const StringPiece16& comment) {
+ // We need to process line by line to clean-up whitespace and append prefixes.
+ for (StringPiece16 line : util::tokenize(comment, u'\n')) {
+ appendCommentLine(util::trimWhitespace(line));
+ }
+}
+
+std::string AnnotationProcessor::buildComment() {
+ mComment += "\n";
+ mComment += mPrefix;
+ mComment += " */";
+ return std::move(mComment);
+}
+
+std::string AnnotationProcessor::buildAnnotations() {
+ return std::move(mAnnotations);
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/java/AnnotationProcessor.h b/tools/aapt2/java/AnnotationProcessor.h
new file mode 100644
index 0000000..b472109
--- /dev/null
+++ b/tools/aapt2/java/AnnotationProcessor.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2015 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 AAPT_JAVA_ANNOTATIONPROCESSOR_H
+#define AAPT_JAVA_ANNOTATIONPROCESSOR_H
+
+#include "util/StringPiece.h"
+
+#include <string>
+
+namespace aapt {
+
+/**
+ * Builds a JavaDoc comment from a set of XML comments.
+ * This will also look for instances of @SystemApi and convert them to
+ * actual Java annotations.
+ *
+ * Example:
+ *
+ * Input XML:
+ *
+ * <!-- This is meant to be hidden because
+ * It is system api. Also it is @deprecated
+ * @SystemApi
+ * -->
+ *
+ * Output JavaDoc:
+ *
+ * /\*
+ * * This is meant to be hidden because
+ * * It is system api. Also it is @deprecated
+ * * @SystemApi
+ * *\/
+ *
+ * Output Annotations:
+ *
+ * @Deprecated
+ * @android.annotation.SystemApi
+ *
+ */
+class AnnotationProcessor {
+public:
+ /**
+ * Creates an AnnotationProcessor with a given prefix for each line generated.
+ * This is usually a set of spaces for indentation.
+ */
+ AnnotationProcessor(const StringPiece& prefix) : mPrefix(prefix.toString()) {
+ }
+
+ /**
+ * Adds more comments. Since resources can have various values with different configurations,
+ * we need to collect all the comments.
+ */
+ void appendComment(const StringPiece16& comment);
+
+ /**
+ * Finishes the comment and moves it to the caller. Subsequent calls to buildComment() have
+ * undefined results.
+ */
+ std::string buildComment();
+
+ /**
+ * Finishes the annotation and moves it to the caller. Subsequent calls to buildAnnotations()
+ * have undefined results.
+ */
+ std::string buildAnnotations();
+
+private:
+ std::string mPrefix;
+ std::string mComment;
+ std::string mAnnotations;
+ bool mDeprecated = false;
+ bool mSystemApi = false;
+
+ void appendCommentLine(const StringPiece16& line);
+};
+
+} // namespace aapt
+
+#endif /* AAPT_JAVA_ANNOTATIONPROCESSOR_H */
diff --git a/tools/aapt2/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
similarity index 98%
rename from tools/aapt2/JavaClassGenerator.cpp
rename to tools/aapt2/java/JavaClassGenerator.cpp
index cdf1b6a..0175489 100644
--- a/tools/aapt2/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -14,11 +14,12 @@
* limitations under the License.
*/
-#include "JavaClassGenerator.h"
#include "NameMangler.h"
#include "Resource.h"
#include "ResourceTable.h"
#include "ResourceValues.h"
+#include "java/AnnotationProcessor.h"
+#include "java/JavaClassGenerator.h"
#include "util/StringPiece.h"
#include <algorithm>
diff --git a/tools/aapt2/JavaClassGenerator.h b/tools/aapt2/java/JavaClassGenerator.h
similarity index 100%
rename from tools/aapt2/JavaClassGenerator.h
rename to tools/aapt2/java/JavaClassGenerator.h
diff --git a/tools/aapt2/JavaClassGenerator_test.cpp b/tools/aapt2/java/JavaClassGenerator_test.cpp
similarity index 99%
rename from tools/aapt2/JavaClassGenerator_test.cpp
rename to tools/aapt2/java/JavaClassGenerator_test.cpp
index cc5e981..3625f9c 100644
--- a/tools/aapt2/JavaClassGenerator_test.cpp
+++ b/tools/aapt2/java/JavaClassGenerator_test.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "JavaClassGenerator.h"
+#include "java/JavaClassGenerator.h"
#include "util/Util.h"
#include "test/Builders.h"
diff --git a/tools/aapt2/java/ManifestClassGenerator.cpp b/tools/aapt2/java/ManifestClassGenerator.cpp
new file mode 100644
index 0000000..c12da64
--- /dev/null
+++ b/tools/aapt2/java/ManifestClassGenerator.cpp
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2015 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 "Source.h"
+#include "XmlDom.h"
+
+#include "java/AnnotationProcessor.h"
+#include "java/ManifestClassGenerator.h"
+#include "util/Maybe.h"
+
+#include <algorithm>
+
+namespace aapt {
+
+constexpr const char16_t* kSchemaAndroid = u"http://schemas.android.com/apk/res/android";
+
+static Maybe<StringPiece16> extractJavaIdentifier(IDiagnostics* diag, const Source& source,
+ const StringPiece16& value) {
+ const StringPiece16 sep = u".";
+ auto iter = std::find_end(value.begin(), value.end(), sep.begin(), sep.end());
+
+ StringPiece16 result;
+ if (iter != value.end()) {
+ result.assign(iter + sep.size(), value.end() - (iter + sep.size()));
+ } else {
+ result = value;
+ }
+
+ if (result.empty()) {
+ diag->error(DiagMessage(source) << "empty symbol");
+ return {};
+ }
+
+ iter = util::findNonAlphaNumericAndNotInSet(result, u"_");
+ if (iter != result.end()) {
+ diag->error(DiagMessage(source)
+ << "invalid character '" << StringPiece16(iter, 1)
+ << "' in '" << result << "'");
+ return {};
+ }
+
+ if (*result.begin() >= u'0' && *result.begin() <= u'9') {
+ diag->error(DiagMessage(source) << "symbol can not start with a digit");
+ return {};
+ }
+
+ return result;
+}
+
+static bool writeSymbol(IDiagnostics* diag, const Source& source, xml::Element* el,
+ std::ostream* out) {
+ xml::Attribute* attr = el->findAttribute(kSchemaAndroid, u"name");
+ if (!attr) {
+ diag->error(DiagMessage(source) << "<" << el->name << "> must define 'android:name'");
+ return false;
+ }
+
+ Maybe<StringPiece16> result = extractJavaIdentifier(diag, source.withLine(el->lineNumber),
+ attr->value);
+ if (!result) {
+ return false;
+ }
+
+ *out << "\n";
+
+ if (!util::trimWhitespace(el->comment).empty()) {
+ AnnotationProcessor processor(" ");
+ processor.appendComment(el->comment);
+ *out << processor.buildComment() << "\n";
+ std::string annotations = processor.buildAnnotations();
+ if (!annotations.empty()) {
+ *out << annotations << "\n";
+ }
+ }
+ *out << " public static final String " << result.value() << "=\"" << attr->value << "\";\n";
+ return true;
+}
+
+bool ManifestClassGenerator::generate(IDiagnostics* diag, const StringPiece16& package,
+ XmlResource* res, std::ostream* out) {
+ xml::Element* el = xml::findRootElement(res->root.get());
+ if (!el) {
+ return false;
+ }
+
+ if (el->name != u"manifest" && !el->namespaceUri.empty()) {
+ diag->error(DiagMessage(res->file.source) << "no <manifest> root tag defined");
+ return false;
+ }
+
+ *out << "package " << package << ";\n\n"
+ << "public class Manifest {\n";
+
+ bool error = false;
+ std::vector<xml::Element*> children = el->getChildElements();
+
+
+ // First write out permissions.
+ *out << " public static class permission {\n";
+ for (xml::Element* childEl : children) {
+ if (childEl->namespaceUri.empty() && childEl->name == u"permission") {
+ error |= !writeSymbol(diag, res->file.source, childEl, out);
+ }
+ }
+ *out << " }\n";
+
+ // Next write out permission groups.
+ *out << " public static class permission_group {\n";
+ for (xml::Element* childEl : children) {
+ if (childEl->namespaceUri.empty() && childEl->name == u"permission-group") {
+ error |= !writeSymbol(diag, res->file.source, childEl, out);
+ }
+ }
+ *out << " }\n";
+
+ *out << "}\n";
+ return !error;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/java/ManifestClassGenerator.h b/tools/aapt2/java/ManifestClassGenerator.h
new file mode 100644
index 0000000..0f0998f
--- /dev/null
+++ b/tools/aapt2/java/ManifestClassGenerator.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 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 AAPT_JAVA_MANIFESTCLASSGENERATOR_H
+#define AAPT_JAVA_MANIFESTCLASSGENERATOR_H
+
+#include "Diagnostics.h"
+#include "process/IResourceTableConsumer.h"
+#include "util/StringPiece.h"
+
+#include <iostream>
+
+namespace aapt {
+
+struct ManifestClassGenerator {
+ bool generate(IDiagnostics* diag, const StringPiece16& package, XmlResource* res,
+ std::ostream* out);
+};
+
+} // namespace aapt
+
+#endif /* AAPT_JAVA_MANIFESTCLASSGENERATOR_H */
diff --git a/tools/aapt2/java/ManifestClassGenerator_test.cpp b/tools/aapt2/java/ManifestClassGenerator_test.cpp
new file mode 100644
index 0000000..1b5bc05
--- /dev/null
+++ b/tools/aapt2/java/ManifestClassGenerator_test.cpp
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2015 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 "java/ManifestClassGenerator.h"
+
+#include "test/Builders.h"
+#include "test/Context.h"
+
+#include <gtest/gtest.h>
+
+namespace aapt {
+
+TEST(ManifestClassGeneratorTest, NameIsProperlyGeneratedFromSymbol) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+ std::unique_ptr<XmlResource> manifest = test::buildXmlDom(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
+ <permission android:name="android.permission.ACCESS_INTERNET" />
+ <permission android:name="android.DO_DANGEROUS_THINGS" />
+ <permission android:name="com.test.sample.permission.HUH" />
+ <permission-group android:name="foo.bar.PERMISSION" />
+ </manifest>)EOF");
+
+ std::stringstream out;
+ ManifestClassGenerator generator;
+ ASSERT_TRUE(generator.generate(context->getDiagnostics(), u"android", manifest.get(), &out));
+
+ std::string actual = out.str();
+
+ const size_t permissionClassPos = actual.find("public static class permission {");
+ const size_t permissionGroupClassPos = actual.find("public static class permission_group {");
+ ASSERT_NE(std::string::npos, permissionClassPos);
+ ASSERT_NE(std::string::npos, permissionGroupClassPos);
+
+ //
+ // Make sure these permissions are in the permission class.
+ //
+
+ size_t pos = actual.find("public static final String ACCESS_INTERNET="
+ "\"android.permission.ACCESS_INTERNET\";");
+ EXPECT_GT(pos, permissionClassPos);
+ EXPECT_LT(pos, permissionGroupClassPos);
+
+ pos = actual.find("public static final String DO_DANGEROUS_THINGS="
+ "\"android.DO_DANGEROUS_THINGS\";");
+ EXPECT_GT(pos, permissionClassPos);
+ EXPECT_LT(pos, permissionGroupClassPos);
+
+ pos = actual.find("public static final String HUH=\"com.test.sample.permission.HUH\";");
+ EXPECT_GT(pos, permissionClassPos);
+ EXPECT_LT(pos, permissionGroupClassPos);
+
+ //
+ // Make sure these permissions are in the permission_group class
+ //
+
+ pos = actual.find("public static final String PERMISSION="
+ "\"foo.bar.PERMISSION\";");
+ EXPECT_GT(pos, permissionGroupClassPos);
+ EXPECT_LT(pos, std::string::npos);
+}
+
+TEST(ManifestClassGeneratorTest, CommentsAndAnnotationsArePresent) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+ std::unique_ptr<XmlResource> manifest = test::buildXmlDom(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
+ <!-- Required to access the internet.
+ Added in API 1. -->
+ <permission android:name="android.permission.ACCESS_INTERNET" />
+ <!-- @deprecated This permission is for playing outside. -->
+ <permission android:name="android.permission.PLAY_OUTSIDE" />
+ <!-- This is a private permission for system only!
+ @hide
+ @SystemApi -->
+ <permission android:name="android.permission.SECRET" />
+ </manifest>)EOF");
+
+ std::stringstream out;
+ ManifestClassGenerator generator;
+ ASSERT_TRUE(generator.generate(context->getDiagnostics(), u"android", manifest.get(), &out));
+
+ std::string actual = out.str();
+
+ EXPECT_NE(std::string::npos, actual.find(
+R"EOF( /**
+ * Required to access the internet.
+ * Added in API 1.
+ */
+ public static final String ACCESS_INTERNET="android.permission.ACCESS_INTERNET";)EOF"));
+
+ EXPECT_NE(std::string::npos, actual.find(
+R"EOF( /**
+ * @deprecated This permission is for playing outside.
+ */
+ @Deprecated
+ public static final String PLAY_OUTSIDE="android.permission.PLAY_OUTSIDE";)EOF"));
+
+ EXPECT_NE(std::string::npos, actual.find(
+R"EOF( /**
+ * This is a private permission for system only!
+ * @hide
+ * @SystemApi
+ */
+ @android.annotations.SystemApi
+ public static final String SECRET="android.permission.SECRET";)EOF"));
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/ProguardRules.cpp b/tools/aapt2/java/ProguardRules.cpp
similarity index 99%
rename from tools/aapt2/ProguardRules.cpp
rename to tools/aapt2/java/ProguardRules.cpp
index 7f4dc91..7683f27 100644
--- a/tools/aapt2/ProguardRules.cpp
+++ b/tools/aapt2/java/ProguardRules.cpp
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-#include "ProguardRules.h"
#include "XmlDom.h"
+#include "java/ProguardRules.h"
#include "util/Util.h"
#include <memory>
diff --git a/tools/aapt2/ProguardRules.h b/tools/aapt2/java/ProguardRules.h
similarity index 100%
rename from tools/aapt2/ProguardRules.h
rename to tools/aapt2/java/ProguardRules.h
diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp
index ad701de..77918ac 100644
--- a/tools/aapt2/link/Link.cpp
+++ b/tools/aapt2/link/Link.cpp
@@ -17,15 +17,16 @@
#include "AppInfo.h"
#include "Debug.h"
#include "Flags.h"
-#include "JavaClassGenerator.h"
#include "NameMangler.h"
-#include "ProguardRules.h"
#include "XmlDom.h"
#include "compile/IdAssigner.h"
#include "flatten/Archive.h"
#include "flatten/TableFlattener.h"
#include "flatten/XmlFlattener.h"
+#include "java/JavaClassGenerator.h"
+#include "java/ManifestClassGenerator.h"
+#include "java/ProguardRules.h"
#include "link/Linkers.h"
#include "link/TableMerger.h"
#include "process/IResourceTableConsumer.h"
@@ -354,6 +355,36 @@
return true;
}
+ bool writeManifestJavaFile(XmlResource* manifestXml) {
+ if (!mOptions.generateJavaClassPath) {
+ return true;
+ }
+
+ std::string outPath = mOptions.generateJavaClassPath.value();
+ file::appendPath(&outPath,
+ file::packageToPath(util::utf16ToUtf8(mContext.getCompilationPackage())));
+ file::mkdirs(outPath);
+ file::appendPath(&outPath, "Manifest.java");
+
+ std::ofstream fout(outPath, std::ofstream::binary);
+ if (!fout) {
+ mContext.getDiagnostics()->error(DiagMessage() << strerror(errno));
+ return false;
+ }
+
+ ManifestClassGenerator generator;
+ if (!generator.generate(mContext.getDiagnostics(), mContext.getCompilationPackage(),
+ manifestXml, &fout)) {
+ return false;
+ }
+
+ if (!fout) {
+ mContext.getDiagnostics()->error(DiagMessage() << strerror(errno));
+ return false;
+ }
+ return true;
+ }
+
bool writeProguardFile(const proguard::KeepSet& keepSet) {
if (!mOptions.generateProguardRulesPath) {
return true;
@@ -548,6 +579,12 @@
error = true;
}
+ if (mOptions.generateJavaClassPath) {
+ if (!writeManifestJavaFile(manifestXml.get())) {
+ error = true;
+ }
+ }
+
if (!flattenXml(manifestXml.get(), "AndroidManifest.xml", {},
archiveWriter.get())) {
error = true;
diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h
index 1b510e7..89cd972 100644
--- a/tools/aapt2/test/Builders.h
+++ b/tools/aapt2/test/Builders.h
@@ -19,8 +19,8 @@
#include "ResourceTable.h"
#include "ResourceValues.h"
-#include "util/Util.h"
#include "XmlDom.h"
+#include "util/Util.h"
#include "test/Common.h"
diff --git a/tools/aapt2/unflatten/BinaryResourceParser.cpp b/tools/aapt2/unflatten/BinaryResourceParser.cpp
index 314c1e8..30c6091 100644
--- a/tools/aapt2/unflatten/BinaryResourceParser.cpp
+++ b/tools/aapt2/unflatten/BinaryResourceParser.cpp
@@ -678,8 +678,6 @@
// fallthrough
case ResourceType::kAttr:
return parseAttr(name, config, map);
- case ResourceType::kIntegerArray:
- // fallthrough
case ResourceType::kArray:
return parseArray(name, config, map);
case ResourceType::kStyleable:
diff --git a/tools/aapt2/util/StringPiece.h b/tools/aapt2/util/StringPiece.h
index 8cbdeae..31deb45 100644
--- a/tools/aapt2/util/StringPiece.h
+++ b/tools/aapt2/util/StringPiece.h
@@ -36,6 +36,7 @@
class BasicStringPiece {
public:
using const_iterator = const TChar*;
+ using difference_type = size_t;
BasicStringPiece();
BasicStringPiece(const BasicStringPiece<TChar>& str);
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 007d075..498be5a 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -513,4 +513,8 @@
// TODO Auto-generated method stub
return null;
}
+
+ @Override
+ public void cancelTaskWindowTransition(int taskId) {
+ }
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
index 2997907..11bd15d 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
@@ -95,6 +95,13 @@
}
@Override
+ public void repositionChild(IWindow childWindow, int x, int y, long deferTransactionUntilFrame,
+ Rect outFrame) {
+ // pass for now.
+ return;
+ }
+
+ @Override
public void performDeferredDestroy(IWindow window) {
// pass for now.
}