summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmds/pm/src/com/android/commands/pm/Pm.java22
-rw-r--r--core/java/android/accounts/AccountManager.java24
-rw-r--r--core/java/android/accounts/IAccountManager.aidl2
-rw-r--r--core/java/android/os/Binder.java2
-rw-r--r--core/java/android/os/IUserManager.aidl1
-rw-r--r--core/java/android/os/Process.java4
-rw-r--r--core/java/android/os/UserHandle.java14
-rw-r--r--core/java/android/os/UserManager.java17
-rw-r--r--core/res/AndroidManifest.xml2
-rw-r--r--core/res/res/values/colors_holo.xml6
-rw-r--r--docs/html/tools/support-library/index.jd2
-rw-r--r--native/graphics/jni/Android.mk2
-rw-r--r--services/core/java/com/android/server/accounts/AccountManagerService.java29
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java2
-rw-r--r--services/core/java/com/android/server/pm/Installer.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageDexOptimizer.java25
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java14
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java21
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/Owners.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java5
-rw-r--r--tests/WindowAnimationJank/Android.mk31
-rw-r--r--tests/WindowAnimationJank/AndroidManifest.xml40
-rw-r--r--tests/WindowAnimationJank/res/layout/flowlayout.xml21
-rw-r--r--tests/WindowAnimationJank/src/android/windowanimationjank/ElementLayoutActivity.java159
-rw-r--r--tests/WindowAnimationJank/src/android/windowanimationjank/FlowLayout.java111
-rw-r--r--tests/WindowAnimationJank/src/android/windowanimationjank/FullscreenRotationTest.java53
-rw-r--r--tests/WindowAnimationJank/src/android/windowanimationjank/Utils.java118
-rw-r--r--tests/WindowAnimationJank/src/android/windowanimationjank/WindowAnimationJankTestBase.java56
28 files changed, 724 insertions, 63 deletions
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 3f0a4445fdce..ebf508514b3c 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -22,11 +22,13 @@ import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATIO
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
+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;
import android.content.IIntentReceiver;
import android.content.IIntentSender;
import android.content.Intent;
@@ -92,6 +94,7 @@ public final class Pm {
IPackageManager mPm;
IPackageInstaller mInstaller;
IUserManager mUm;
+ IAccountManager mAm;
private WeakHashMap<String, Resources> mResourceCache
= new WeakHashMap<String, Resources>();
@@ -122,9 +125,10 @@ public final class Pm {
if (args.length < 1) {
return showUsage();
}
-
- mUm = IUserManager.Stub.asInterface(ServiceManager.getService("user"));
+ mAm = IAccountManager.Stub.asInterface(ServiceManager.getService(Context.ACCOUNT_SERVICE));
+ mUm = IUserManager.Stub.asInterface(ServiceManager.getService(Context.USER_SERVICE));
mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
+
if (mPm == null) {
System.err.println(PM_NOT_RUNNING_ERR);
return 1;
@@ -1381,6 +1385,8 @@ public final class Pm {
}
} else if ("--managed".equals(opt)) {
flags |= UserInfo.FLAG_MANAGED_PROFILE;
+ } else if ("--restricted".equals(opt)) {
+ flags |= UserInfo.FLAG_RESTRICTED;
} else {
System.err.println("Error: unknown option " + opt);
showUsage();
@@ -1394,12 +1400,18 @@ public final class Pm {
}
name = arg;
try {
- UserInfo info = null;
- if (userId < 0) {
+ UserInfo info;
+ if ((flags & UserInfo.FLAG_RESTRICTED) != 0) {
+ // In non-split user mode, userId can only be SYSTEM
+ int parentUserId = userId >= 0 ? userId : UserHandle.USER_SYSTEM;
+ info = mUm.createRestrictedProfile(name, parentUserId);
+ mAm.addSharedAccountsFromParentUser(userId, parentUserId);
+ } else if (userId < 0) {
info = mUm.createUser(name, flags);
} else {
info = mUm.createProfileForUser(name, flags, userId);
}
+
if (info != null) {
System.out.println("Success: created user id " + info.id);
return 1;
@@ -2122,7 +2134,7 @@ public final class Pm {
System.err.println(" pm get-install-location");
System.err.println(" pm set-permission-enforced PERMISSION [true|false]");
System.err.println(" pm trim-caches DESIRED_FREE_SPACE [internal|UUID]");
- System.err.println(" pm create-user [--profileOf USER_ID] [--managed] USER_NAME");
+ System.err.println(" pm create-user [--profileOf USER_ID] [--managed] [--restricted] USER_NAME");
System.err.println(" pm remove-user USER_ID");
System.err.println(" pm get-max-users");
System.err.println("");
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index d751f96f65f6..0a7568a8c876 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -16,6 +16,7 @@
package android.accounts;
+import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.Size;
import android.app.Activity;
@@ -423,6 +424,7 @@ public class AccountManager {
* @return An array of {@link Account}, one for each account. Empty
* (never null) if no accounts have been added.
*/
+ @NonNull
@RequiresPermission(GET_ACCOUNTS)
public Account[] getAccounts() {
try {
@@ -448,6 +450,7 @@ public class AccountManager {
* @return An array of {@link Account}, one for each account. Empty
* (never null) if no accounts have been added.
*/
+ @NonNull
@RequiresPermission(GET_ACCOUNTS)
public Account[] getAccountsAsUser(int userId) {
try {
@@ -466,6 +469,7 @@ public class AccountManager {
* @param uid the uid of the calling app.
* @return the accounts that are available to this package and user.
*/
+ @NonNull
public Account[] getAccountsForPackage(String packageName, int uid) {
try {
return mService.getAccountsForPackage(packageName, uid, mContext.getOpPackageName());
@@ -483,6 +487,7 @@ public class AccountManager {
* @return An array of {@link Account}, one per matching account. Empty
* (never null) if no accounts of the specified type have been added.
*/
+ @NonNull
public Account[] getAccountsByTypeForPackage(String type, String packageName) {
try {
return mService.getAccountsByTypeForPackage(type, packageName,
@@ -515,12 +520,14 @@ public class AccountManager {
* @return An array of {@link Account}, one per matching account. Empty
* (never null) if no accounts of the specified type have been added.
*/
+ @NonNull
@RequiresPermission(GET_ACCOUNTS)
public Account[] getAccountsByType(String type) {
return getAccountsByTypeAsUser(type, Process.myUserHandle());
}
/** @hide Same as {@link #getAccountsByType(String)} but for a specific user. */
+ @NonNull
public Account[] getAccountsByTypeAsUser(String type, UserHandle userHandle) {
try {
return mService.getAccountsAsUser(type, userHandle.getIdentifier(),
@@ -1537,23 +1544,22 @@ public class AccountManager {
}.start();
}
+
/**
- * Adds a shared account from the primary user to a secondary user. Adding the shared account
+ * Adds shared accounts from a parent user to a secondary user. Adding the shared account
* doesn't take effect immediately. When the target user starts up, any pending shared accounts
* are attempted to be copied to the target user from the primary via calls to the
* authenticator.
- * @param account the account to share
- * @param user the target user
- * @return
+ * @param parentUser parent user
+ * @param user target user
* @hide
*/
- public boolean addSharedAccount(final Account account, UserHandle user) {
+ public void addSharedAccountsFromParentUser(UserHandle parentUser, UserHandle user) {
try {
- boolean val = mService.addSharedAccountAsUser(account, user.getIdentifier());
- return val;
+ mService.addSharedAccountsFromParentUser(parentUser.getIdentifier(),
+ user.getIdentifier());
} catch (RemoteException re) {
- // won't ever happen
- throw new RuntimeException(re);
+ throw new IllegalStateException(re);
}
}
diff --git a/core/java/android/accounts/IAccountManager.aidl b/core/java/android/accounts/IAccountManager.aidl
index 4378df408d10..0d95db1d8302 100644
--- a/core/java/android/accounts/IAccountManager.aidl
+++ b/core/java/android/accounts/IAccountManager.aidl
@@ -74,9 +74,9 @@ interface IAccountManager {
String authTokenType);
/* Shared accounts */
- boolean addSharedAccountAsUser(in Account account, int userId);
Account[] getSharedAccountsAsUser(int userId);
boolean removeSharedAccountAsUser(in Account account, int userId);
+ void addSharedAccountsFromParentUser(int parentUserId, int userId);
/* Account renaming. */
void renameAccount(in IAccountManagerResponse response, in Account accountToRename, String newName);
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index cfa6164b0454..c4501bade098 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -157,7 +157,7 @@ public class Binder implements IBinder {
* incoming transaction, then its own UserHandle is returned.
*/
public static final UserHandle getCallingUserHandle() {
- return new UserHandle(UserHandle.getUserId(getCallingUid()));
+ return UserHandle.of(UserHandle.getUserId(getCallingUid()));
}
/**
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index aeb5d4517e6c..7c091575df38 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -35,6 +35,7 @@ interface IUserManager {
UserInfo createUser(in String name, int flags);
UserInfo createProfileForUser(in String name, int flags, int userHandle);
+ UserInfo createRestrictedProfile(String name, int parentUserId);
void setUserEnabled(int userHandle);
boolean removeUser(int userHandle);
void setUserName(int userHandle, String name);
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 7234e985757a..4ac361d00682 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -797,8 +797,8 @@ public class Process {
* {@link #myUid()} in that a particular user will have multiple
* distinct apps running under it each with their own uid.
*/
- public static final UserHandle myUserHandle() {
- return new UserHandle(UserHandle.getUserId(myUid()));
+ public static UserHandle myUserHandle() {
+ return UserHandle.of(UserHandle.getUserId(myUid()));
}
/**
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index 213e0831c0f2..796addc4c3bc 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -17,7 +17,6 @@
package android.os;
import android.annotation.SystemApi;
-import android.util.SparseArray;
import java.io.PrintWriter;
@@ -83,8 +82,6 @@ public final class UserHandle implements Parcelable {
final int mHandle;
- private static final SparseArray<UserHandle> userHandles = new SparseArray<UserHandle>();
-
/**
* Checks to see if the user id is the same for the two uids, i.e., they belong to the same
* user.
@@ -144,15 +141,8 @@ public final class UserHandle implements Parcelable {
}
/** @hide */
- public static UserHandle getCallingUserHandle() {
- int userId = getUserId(Binder.getCallingUid());
- UserHandle userHandle = userHandles.get(userId);
- // Intentionally not synchronized to save time
- if (userHandle == null) {
- userHandle = new UserHandle(userId);
- userHandles.put(userId, userHandle);
- }
- return userHandle;
+ public static UserHandle of(int userId) {
+ return userId == USER_SYSTEM ? SYSTEM : new UserHandle(userId);
}
/**
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 64e2505b5006..f58933a82186 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -15,6 +15,7 @@
*/
package android.os;
+import android.accounts.AccountManager;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.app.ActivityManager;
@@ -927,7 +928,8 @@ public class UserManager {
}
/**
- * Creates a restricted profile with the specified name.
+ * Creates a restricted profile with the specified name. This method also sets necessary
+ * restrictions and adds shared accounts.
*
* @param name profile's name
* @return UserInfo object for the created user, or null if the user could not be created.
@@ -935,13 +937,14 @@ public class UserManager {
*/
public UserInfo createRestrictedProfile(String name) {
try {
- if (isSplitSystemUser()) {
- return mService.createProfileForUser(name, UserInfo.FLAG_RESTRICTED,
- UserHandle.getCallingUserId());
- } else {
- return mService.createProfileForUser(name, UserInfo.FLAG_RESTRICTED,
- UserHandle.USER_SYSTEM);
+ UserHandle parentUserHandle = Process.myUserHandle();
+ UserInfo user = mService.createRestrictedProfile(name,
+ parentUserHandle.getIdentifier());
+ if (user != null) {
+ AccountManager.get(mContext).addSharedAccountsFromParentUser(parentUserHandle,
+ UserHandle.of(user.id));
}
+ return user;
} catch (RemoteException e) {
Log.w(TAG, "Could not create a restricted profile", e);
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index c7134548a1e1..df5e35968af7 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1715,7 +1715,7 @@
android:protectionLevel="signature|privileged" />
<!-- Allows applications to change network connectivity state.
- <p>Protection level: normal
+ <p>Protection level: signature
-->
<permission android:name="android.permission.CHANGE_NETWORK_STATE"
android:description="@string/permdesc_changeNetworkState"
diff --git a/core/res/res/values/colors_holo.xml b/core/res/res/values/colors_holo.xml
index c29fec652652..9d1dda547586 100644
--- a/core/res/res/values/colors_holo.xml
+++ b/core/res/res/values/colors_holo.xml
@@ -79,8 +79,7 @@
<eat-comment />
<color name="holo_primary_dark">#ff000000</color>
- <color name="holo_primary">#ffe6e6e6</color>
- <color name="holo_primary_light">#ffffffff</color>
+ <color name="holo_primary">#ff222222</color>
<color name="holo_control_activated">@color/holo_blue_light</color>
<color name="holo_control_normal">#39cccccc</color>
<color name="holo_button_pressed">#59f0f0f0</color>
@@ -88,8 +87,7 @@
<color name="holo_light_primary_dark">#ff000000</color>
<color name="holo_light_primary">#ffe6e6e6</color>
- <color name="holo_light_primary_light">#ffffffff</color>
- <color name="holo_light_control_activated">@color/holo_control_activated</color>
+ <color name="holo_light_control_activated">@color/holo_blue_light</color>
<color name="holo_light_control_normal">#dacccccc</color>
<color name="holo_light_button_pressed">#66666666</color>
<color name="holo_light_button_normal">#b3cccccc</color>
diff --git a/docs/html/tools/support-library/index.jd b/docs/html/tools/support-library/index.jd
index 9dc0ed154208..22ad0c9c4539 100644
--- a/docs/html/tools/support-library/index.jd
+++ b/docs/html/tools/support-library/index.jd
@@ -665,7 +665,7 @@ page.title=Support Library
<li>Added support for a Collapse icon description in the {@link android.support.v7.widget.Toolbar}
class.</li>
<li>Updated the {@link android.support.v7.widget.SearchView} widget to support displaying
- the {@link android.support.v7.mediarouter.R.attr#commitIcon}. </li>
+ the {@link android.support.v7.appcompat.R.attr#commitIcon}. </li>
<li>Removed the <code>buttonGravity</code> attribute from the
{@link android.support.v7.widget.Toolbar} class. </li>
</ul>
diff --git a/native/graphics/jni/Android.mk b/native/graphics/jni/Android.mk
index 1b684bb4e28b..175f73007484 100644
--- a/native/graphics/jni/Android.mk
+++ b/native/graphics/jni/Android.mk
@@ -31,7 +31,7 @@ LOCAL_MODULE:= libjnigraphics
LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
-# TODO: This is to work around b/19059885. Remove after root cause is fixed
+# TODO: This is to work around b/24465209. Remove after root cause is fixed
LOCAL_LDFLAGS_arm := -Wl,--hash-style=both
include $(BUILD_SHARED_LIBRARY)
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index dbf128840c59..6b346123e94a 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -29,6 +29,7 @@ import android.accounts.IAccountAuthenticator;
import android.accounts.IAccountAuthenticatorResponse;
import android.accounts.IAccountManager;
import android.accounts.IAccountManagerResponse;
+import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.AppGlobals;
@@ -2526,6 +2527,7 @@ public class AccountManagerService
* Returns the accounts visible to the client within the context of a specific user
* @hide
*/
+ @NonNull
public Account[] getAccounts(int userId, String opPackageName) {
int callingUid = Binder.getCallingUid();
List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
@@ -2551,6 +2553,7 @@ public class AccountManagerService
*
* @hide
*/
+ @NonNull
public AccountAndUser[] getRunningAccounts() {
final int[] runningUserIds;
try {
@@ -2563,6 +2566,7 @@ public class AccountManagerService
}
/** {@hide} */
+ @NonNull
public AccountAndUser[] getAllAccounts() {
final List<UserInfo> users = getUserManager().getUsers();
final int[] userIds = new int[users.size()];
@@ -2572,6 +2576,7 @@ public class AccountManagerService
return getAccounts(userIds);
}
+ @NonNull
private AccountAndUser[] getAccounts(int[] userIds) {
final ArrayList<AccountAndUser> runningAccounts = Lists.newArrayList();
for (int userId : userIds) {
@@ -2591,10 +2596,12 @@ public class AccountManagerService
}
@Override
+ @NonNull
public Account[] getAccountsAsUser(String type, int userId, String opPackageName) {
return getAccountsAsUser(type, userId, null, -1, opPackageName);
}
+ @NonNull
private Account[] getAccountsAsUser(
String type,
int userId,
@@ -2649,6 +2656,7 @@ public class AccountManagerService
}
}
+ @NonNull
private Account[] getAccountsInternal(
UserAccounts userAccounts,
int callingUid,
@@ -2672,7 +2680,15 @@ public class AccountManagerService
}
@Override
- public boolean addSharedAccountAsUser(Account account, int userId) {
+ public void addSharedAccountsFromParentUser(int parentUserId, int userId) {
+ checkManageUsersPermission("addSharedAccountsFromParentUser");
+ Account[] accounts = getAccountsAsUser(null, parentUserId, mContext.getOpPackageName());
+ for (Account account : accounts) {
+ addSharedAccountAsUser(account, userId);
+ }
+ }
+
+ private boolean addSharedAccountAsUser(Account account, int userId) {
userId = handleIncomingUser(userId);
UserAccounts accounts = getUserAccounts(userId);
SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
@@ -2764,11 +2780,13 @@ public class AccountManagerService
}
@Override
+ @NonNull
public Account[] getAccounts(String type, String opPackageName) {
return getAccountsAsUser(type, UserHandle.getCallingUserId(), opPackageName);
}
@Override
+ @NonNull
public Account[] getAccountsForPackage(String packageName, int uid, String opPackageName) {
int callingUid = Binder.getCallingUid();
if (!UserHandle.isSameApp(callingUid, Process.myUid())) {
@@ -2780,6 +2798,7 @@ public class AccountManagerService
}
@Override
+ @NonNull
public Account[] getAccountsByTypeForPackage(String type, String packageName,
String opPackageName) {
int packageUid = -1;
@@ -3844,6 +3863,14 @@ public class AccountManagerService
return false;
}
+ private static void checkManageUsersPermission(String message) {
+ if (ActivityManager.checkComponentPermission(
+ android.Manifest.permission.MANAGE_USERS, Binder.getCallingUid(), -1, true)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("You need MANAGE_USERS permission to: " + message);
+ }
+ }
+
private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType,
int callerUid) {
if (callerUid == Process.SYSTEM_UID) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 0c884f1558fa..14a2cbc0e591 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1684,7 +1684,7 @@ public class NotificationManagerService extends SystemService {
public boolean matchesCallFilter(Bundle extras) {
enforceSystemOrSystemUI("INotificationManager.matchesCallFilter");
return mZenModeHelper.matchesCallFilter(
- UserHandle.getCallingUserHandle(),
+ Binder.getCallingUserHandle(),
extras,
mRankingHelper.findExtractor(ValidateNotificationPeople.class),
MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS,
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index d55697b40720..d8676167497f 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -43,6 +43,8 @@ public final class Installer extends SystemService {
public static final int DEXOPT_DEBUGGABLE = 1 << 3;
/** The system boot has finished */
public static final int DEXOPT_BOOTCOMPLETE = 1 << 4;
+ /** Run the application with the JIT compiler */
+ public static final int DEXOPT_USEJIT = 1 << 5;
private final InstallerConnection mInstaller;
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 8be670e71a24..6c6871fd8dfd 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -39,6 +39,7 @@ import static com.android.server.pm.Installer.DEXOPT_BOOTCOMPLETE;
import static com.android.server.pm.Installer.DEXOPT_DEBUGGABLE;
import static com.android.server.pm.Installer.DEXOPT_PUBLIC;
import static com.android.server.pm.Installer.DEXOPT_SAFEMODE;
+import static com.android.server.pm.Installer.DEXOPT_USEJIT;
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
@@ -75,7 +76,8 @@ final class PackageDexOptimizer {
* {@link PackageManagerService#mInstallLock}.
*/
int performDexOpt(PackageParser.Package pkg, String[] instructionSets,
- boolean forceDex, boolean defer, boolean inclDependencies, boolean bootComplete) {
+ boolean forceDex, boolean defer, boolean inclDependencies,
+ boolean bootComplete, boolean useJit) {
ArraySet<String> done;
if (inclDependencies && (pkg.usesLibraries != null || pkg.usesOptionalLibraries != null)) {
done = new ArraySet<String>();
@@ -90,7 +92,8 @@ final class PackageDexOptimizer {
mDexoptWakeLock.acquire();
}
try {
- return performDexOptLI(pkg, instructionSets, forceDex, defer, bootComplete, done);
+ return performDexOptLI(pkg, instructionSets, forceDex, defer, bootComplete,
+ useJit, done);
} finally {
if (useLock) {
mDexoptWakeLock.release();
@@ -100,7 +103,8 @@ final class PackageDexOptimizer {
}
private int performDexOptLI(PackageParser.Package pkg, String[] targetInstructionSets,
- boolean forceDex, boolean defer, boolean bootComplete, ArraySet<String> done) {
+ boolean forceDex, boolean defer, boolean bootComplete, boolean useJit,
+ ArraySet<String> done) {
final String[] instructionSets = targetInstructionSets != null ?
targetInstructionSets : getAppDexInstructionSets(pkg.applicationInfo);
@@ -108,11 +112,11 @@ final class PackageDexOptimizer {
done.add(pkg.packageName);
if (pkg.usesLibraries != null) {
performDexOptLibsLI(pkg.usesLibraries, instructionSets, forceDex, defer,
- bootComplete, done);
+ bootComplete, useJit, done);
}
if (pkg.usesOptionalLibraries != null) {
performDexOptLibsLI(pkg.usesOptionalLibraries, instructionSets, forceDex, defer,
- bootComplete, done);
+ bootComplete, useJit, done);
}
}
@@ -179,13 +183,15 @@ final class PackageDexOptimizer {
Log.i(TAG, "Running dexopt (" + dexoptType + ") on: " + path + " pkg="
+ pkg.applicationInfo.packageName + " isa=" + dexCodeInstructionSet
+ " vmSafeMode=" + vmSafeMode + " debuggable=" + debuggable
- + " oatDir = " + oatDir + " bootComplete=" + bootComplete);
+ + " oatDir = " + oatDir + " bootComplete=" + bootComplete
+ + " useJit=" + useJit);
final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
final int dexFlags =
(!pkg.isForwardLocked() ? DEXOPT_PUBLIC : 0)
| (vmSafeMode ? DEXOPT_SAFEMODE : 0)
| (debuggable ? DEXOPT_DEBUGGABLE : 0)
- | (bootComplete ? DEXOPT_BOOTCOMPLETE : 0);
+ | (bootComplete ? DEXOPT_BOOTCOMPLETE : 0)
+ | (useJit ? DEXOPT_USEJIT : 0);
final int ret = mPackageManagerService.mInstaller.dexopt(path, sharedGid,
pkg.packageName, dexCodeInstructionSet, dexoptNeeded, oatDir, dexFlags);
@@ -244,12 +250,13 @@ final class PackageDexOptimizer {
}
private void performDexOptLibsLI(ArrayList<String> libs, String[] instructionSets,
- boolean forceDex, boolean defer, boolean bootComplete, ArraySet<String> done) {
+ boolean forceDex, boolean defer, boolean bootComplete, boolean useJit,
+ ArraySet<String> done) {
for (String libName : libs) {
PackageParser.Package libPkg = mPackageManagerService.findSharedNonSystemLibrary(
libName);
if (libPkg != null && !done.contains(libName)) {
- performDexOptLI(libPkg, instructionSets, forceDex, defer, bootComplete, done);
+ performDexOptLI(libPkg, instructionSets, forceDex, defer, bootComplete, useJit, done);
}
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 7a445a6dc6ae..c729e28dae82 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -6251,7 +6251,7 @@ public class PackageManagerService extends IPackageManager.Stub {
synchronized (mInstallLock) {
mPackageDexOptimizer.performDexOpt(p, null /* instruction sets */,
false /* force dex */, false /* defer */, true /* include dependencies */,
- false /* boot complete */);
+ false /* boot complete */, false /*useJit*/);
}
}
@@ -6311,7 +6311,7 @@ public class PackageManagerService extends IPackageManager.Stub {
final String[] instructionSets = new String[] { targetInstructionSet };
int result = mPackageDexOptimizer.performDexOpt(p, instructionSets,
false /* forceDex */, false /* defer */, true /* inclDependencies */,
- true /* boot complete */);
+ true /* boot complete */, false /*useJit*/);
return result == PackageDexOptimizer.DEX_OPT_PERFORMED;
}
} finally {
@@ -6362,7 +6362,7 @@ public class PackageManagerService extends IPackageManager.Stub {
final int res = mPackageDexOptimizer.performDexOpt(pkg, instructionSets,
true /*forceDex*/, false /* defer */, true /* inclDependencies */,
- true /* boot complete */);
+ true /* boot complete */, false /*useJit*/);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
if (res != PackageDexOptimizer.DEX_OPT_PERFORMED) {
@@ -7179,7 +7179,7 @@ public class PackageManagerService extends IPackageManager.Stub {
int result = mPackageDexOptimizer.performDexOpt(pkg, null /* instruction sets */,
forceDex, (scanFlags & SCAN_DEFER_DEX) != 0, false /* inclDependencies */,
- (scanFlags & SCAN_BOOTING) == 0);
+ (scanFlags & SCAN_BOOTING) == 0, false /*useJit*/);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
@@ -7260,7 +7260,7 @@ public class PackageManagerService extends IPackageManager.Stub {
int result = mPackageDexOptimizer.performDexOpt(clientPkg,
null /* instruction sets */, forceDex,
(scanFlags & SCAN_DEFER_DEX) != 0, false,
- (scanFlags & SCAN_BOOTING) == 0);
+ (scanFlags & SCAN_BOOTING) == 0, false /*useJit*/);
if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
throw new PackageManagerException(INSTALL_FAILED_DEXOPT,
"scanPackageLI failed to dexopt clientLibPkgs");
@@ -7884,7 +7884,7 @@ public class PackageManagerService extends IPackageManager.Stub {
int result = mPackageDexOptimizer.performDexOpt(ps.pkg,
null /* instruction sets */, forceDexOpt, deferDexOpt, true,
- bootComplete);
+ bootComplete, false /*useJit*/);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
@@ -12575,7 +12575,7 @@ public class PackageManagerService extends IPackageManager.Stub {
int result = mPackageDexOptimizer
.performDexOpt(pkg, null /* instruction sets */, false /* forceDex */,
false /* defer */, false /* inclDependencies */,
- true /*bootComplete*/);
+ true /*bootComplete*/, false /*useJit*/);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
res.setError(INSTALL_FAILED_DEXOPT, "Dexopt failed for " + pkg.codePath);
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 1924bab6fac6..0577d5905085 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -16,6 +16,7 @@
package com.android.server.pm;
+import android.accounts.Account;
import android.annotation.NonNull;
import android.app.Activity;
import android.app.ActivityManager;
@@ -63,6 +64,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IAppOpsService;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;
+import com.android.server.accounts.AccountManagerService;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -1386,6 +1388,25 @@ public class UserManagerService extends IUserManager.Stub {
}
/**
+ * @hide
+ */
+ public UserInfo createRestrictedProfile(String name, int parentUserId) {
+ checkManageUsersPermission("setupRestrictedProfile");
+ final UserInfo user = createProfileForUser(name, UserInfo.FLAG_RESTRICTED, parentUserId);
+ if (user == null) {
+ return null;
+ }
+ setUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS, true, user.id);
+ // Change the setting before applying the DISALLOW_SHARE_LOCATION restriction, otherwise
+ // the putIntForUser() will fail.
+ android.provider.Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ android.provider.Settings.Secure.LOCATION_MODE,
+ android.provider.Settings.Secure.LOCATION_MODE_OFF, user.id);
+ setUserRestriction(UserManager.DISALLOW_SHARE_LOCATION, true, user.id);
+ return user;
+ }
+
+ /**
* Find the current guest user. If the Guest user is partial,
* then do not include it in the results as it is about to die.
*/
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index a1690828a4df..370cf48b4655 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -101,7 +101,7 @@ class Owners {
public Owners(Context context) {
mContext = context;
- mUserManager = UserManager.get(mContext);
+ mUserManager = context.getSystemService(UserManager.class);
}
/**
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index f3d91600251e..3b30a37bd2c0 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -219,6 +219,11 @@ public class DpmMockContext extends MockContext {
}
@Override
+ public String getSystemServiceName(Class<?> serviceClass) {
+ return realTestContext.getSystemServiceName(serviceClass);
+ }
+
+ @Override
public PackageManager getPackageManager() {
return packageManager;
}
diff --git a/tests/WindowAnimationJank/Android.mk b/tests/WindowAnimationJank/Android.mk
new file mode 100644
index 000000000000..888ae6434516
--- /dev/null
+++ b/tests/WindowAnimationJank/Android.mk
@@ -0,0 +1,31 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := WindowAnimationJank
+
+LOCAL_STATIC_JAVA_LIBRARIES := ub-uiautomator ub-janktesthelper
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_PACKAGE)
diff --git a/tests/WindowAnimationJank/AndroidManifest.xml b/tests/WindowAnimationJank/AndroidManifest.xml
new file mode 100644
index 000000000000..d7aef3348af3
--- /dev/null
+++ b/tests/WindowAnimationJank/AndroidManifest.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ * 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.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.windowanimationjank">
+
+ <uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />
+
+ <application>
+ <uses-library android:name="android.test.runner"/>
+ <activity android:name="ElementLayoutActivity"
+ android:label="ElementLayoutActivity"
+ android:taskAffinity="android.windowanimationjank.ElementLayoutActivity" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+ <!-- self-instrumenting test package. -->
+ <instrumentation android:name="android.test.InstrumentationTestRunner"
+ android:targetPackage="android.windowanimationjank">
+ </instrumentation>
+
+</manifest>
diff --git a/tests/WindowAnimationJank/res/layout/flowlayout.xml b/tests/WindowAnimationJank/res/layout/flowlayout.xml
new file mode 100644
index 000000000000..f2b559b2dd02
--- /dev/null
+++ b/tests/WindowAnimationJank/res/layout/flowlayout.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+ -->
+<android.windowanimationjank.FlowLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/root_flow_layout"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent" /> \ No newline at end of file
diff --git a/tests/WindowAnimationJank/src/android/windowanimationjank/ElementLayoutActivity.java b/tests/WindowAnimationJank/src/android/windowanimationjank/ElementLayoutActivity.java
new file mode 100644
index 000000000000..3b1fabcb59ec
--- /dev/null
+++ b/tests/WindowAnimationJank/src/android/windowanimationjank/ElementLayoutActivity.java
@@ -0,0 +1,159 @@
+/*
+ * 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.windowanimationjank;
+
+import java.util.Random;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.ViewTreeObserver.OnPreDrawListener;
+import android.widget.Chronometer;
+import android.widget.RadioButton;
+import android.widget.Switch;
+import android.widget.TextView;
+import android.widget.ToggleButton;
+
+/*
+ * Activity with arbitrary number of random UI elements, refresh itself constantly.
+ */
+public class ElementLayoutActivity extends Activity implements OnPreDrawListener {
+ public final static String NUM_ELEMENTS_KEY = "num_elements";
+
+ private final static int DEFAULT_NUM_ELEMENTS = 100;
+ private final static int BACKGROUND_COLOR = 0xfffff000;
+ private final static int INDICATOR_COLOR = 0xffff0000;
+
+ private FlowLayout mLayout;
+ // Use the constant seed in order to get predefined order of elements.
+ private Random mRandom = new Random(0);
+ // Blinker indicator for visual feedback that Activity is currently updating.
+ private TextView mIndicator;
+ private static float mIndicatorState;
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.flowlayout);
+
+ mLayout = (FlowLayout)findViewById(R.id.root_flow_layout);
+ mLayout.setBackgroundColor(BACKGROUND_COLOR);
+
+ mIndicator = new TextView(this);
+ mLayout.addView(mIndicator);
+ mIndicator.setText("***\n***");
+ mIndicator.setBackgroundColor(BACKGROUND_COLOR);
+ mIndicatorState = 0.0f;
+
+ // Need constantly invalidate view in order to get max redraw rate.
+ mLayout.getViewTreeObserver().addOnPreDrawListener(this);
+
+ // Read requested number of elements in layout.
+ int numElements = getIntent().getIntExtra(NUM_ELEMENTS_KEY, DEFAULT_NUM_ELEMENTS);
+
+ for (int i = 0; i < numElements; ++i) {
+ switch (mRandom.nextInt(5)) {
+ case 0:
+ createRadioButton();
+ break;
+ case 1:
+ createToggleButton();
+ break;
+ case 2:
+ createSwitch();
+ break;
+ case 3:
+ createTextView();
+ break;
+ case 4:
+ createChronometer();
+ break;
+ }
+ }
+
+ setContentView(mLayout);
+ }
+
+ private void createTextView() {
+ TextView textView = new TextView(this);
+ int lineCnt = mRandom.nextInt(4);
+ StringBuffer buffer = new StringBuffer();
+ for (int i = 0; i < lineCnt; ++i) {
+ if (i != 0) {
+ buffer.append("\n");
+ }
+ buffer.append("Line:" + mRandom.nextInt());
+ }
+ textView.setText(buffer);
+ mLayout.addView(textView);
+ }
+
+ private void createRadioButton() {
+ RadioButton button = new RadioButton(this);
+ button.setText("RadioButton:" + mRandom.nextInt());
+ mLayout.addView(button);
+ }
+
+ private void createToggleButton() {
+ ToggleButton button = new ToggleButton(this);
+ button.setChecked(mRandom.nextBoolean());
+ mLayout.addView(button);
+ }
+
+ private void createSwitch() {
+ Switch button = new Switch(this);
+ button.setChecked(mRandom.nextBoolean());
+ mLayout.addView(button);
+ }
+
+ private void createChronometer() {
+ Chronometer chronometer = new Chronometer(this);
+ chronometer.setBase(mRandom.nextLong());
+ mLayout.addView(chronometer);
+ chronometer.start();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ }
+
+ @Override
+ public boolean onPreDraw() {
+ // Interpolate indicator color
+ int background = 0xff000000;
+ for (int i = 0; i < 3; ++i) {
+ int shift = 8 * i;
+ int colorB = (BACKGROUND_COLOR >> shift) & 0xff;
+ int colorI = (INDICATOR_COLOR >> shift) & 0xff;
+ int color = (int)((float)colorB * (1.0f - mIndicatorState) +
+ (float)colorI * mIndicatorState);
+ if (color > 255) {
+ color = 255;
+ }
+ background |= (color << shift);
+ }
+
+ mIndicator.setBackgroundColor(background);
+ mIndicatorState += (3 / 60.0f); // around 3 times per second
+ mIndicatorState = mIndicatorState - (int)mIndicatorState;
+
+ mLayout.postInvalidate();
+ return true;
+ }
+} \ No newline at end of file
diff --git a/tests/WindowAnimationJank/src/android/windowanimationjank/FlowLayout.java b/tests/WindowAnimationJank/src/android/windowanimationjank/FlowLayout.java
new file mode 100644
index 000000000000..9a2b9ccb4f90
--- /dev/null
+++ b/tests/WindowAnimationJank/src/android/windowanimationjank/FlowLayout.java
@@ -0,0 +1,111 @@
+/*
+ * 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.windowanimationjank;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Custom layout that place all elements in flows with and automatically wraps them.
+ */
+public class FlowLayout extends ViewGroup {
+ private int mLineHeight;
+
+ public FlowLayout(Context context) {
+ super(context);
+ }
+
+ public FlowLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ final int width =
+ MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() -getPaddingRight();
+ int height =
+ MeasureSpec.getSize(heightMeasureSpec) - getPaddingTop() - getPaddingBottom();
+ final int count = getChildCount();
+
+ int x = getPaddingLeft();
+ int y = getPaddingTop();
+ int lineHeight = 0;
+
+ int childHeightMeasureSpec;
+ if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
+ childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
+ } else {
+ childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ }
+
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() != GONE) {
+ child.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST),
+ childHeightMeasureSpec);
+ final int childWidth = child.getMeasuredWidth();
+ lineHeight = Math.max(lineHeight, child.getMeasuredHeight());
+
+ if (x + childWidth > width) {
+ x = getPaddingLeft();
+ y += lineHeight;
+ }
+
+ x += childWidth;
+ }
+ }
+ mLineHeight = lineHeight;
+
+ if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.UNSPECIFIED) {
+ height = y + lineHeight;
+ } else if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
+ if (y + lineHeight < height) {
+ height = y + lineHeight;
+ }
+ }
+ setMeasuredDimension(width, height);
+ }
+
+ @Override
+ protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
+ if (p instanceof LayoutParams) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ final int count = getChildCount();
+ final int width = r - l;
+ int x = getPaddingLeft();
+ int y = getPaddingTop();
+
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() != GONE) {
+ final int childWidth = child.getMeasuredWidth();
+ final int childHeight = child.getMeasuredHeight();
+ if (x + childWidth > width) {
+ x = getPaddingLeft();
+ y += mLineHeight;
+ }
+ child.layout(x, y, x + childWidth, y + childHeight);
+ x += childWidth;
+ }
+ }
+ }
+}
diff --git a/tests/WindowAnimationJank/src/android/windowanimationjank/FullscreenRotationTest.java b/tests/WindowAnimationJank/src/android/windowanimationjank/FullscreenRotationTest.java
new file mode 100644
index 000000000000..1fb502a09874
--- /dev/null
+++ b/tests/WindowAnimationJank/src/android/windowanimationjank/FullscreenRotationTest.java
@@ -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.
+ */
+
+package android.windowanimationjank;
+
+import android.os.Bundle;
+import android.support.test.jank.JankTest;
+import android.support.test.jank.GfxMonitor;
+
+/**
+ * Detect janks during screen rotation for full-screen activity. Periodically change
+ * orientation from left to right and track ElementLayoutActivity rendering performance
+ * via GfxMonitor.
+ */
+public class FullscreenRotationTest extends WindowAnimationJankTestBase {
+ private final static int STEP_CNT = 3;
+
+ @Override
+ public void beforeTest() throws Exception {
+ getUiDevice().setOrientationLeft();
+ Utils.startElementLayout(getInstrumentation(), 100);
+ super.beforeTest();
+ }
+
+ @Override
+ public void afterTest(Bundle metrics) {
+ Utils.rotateDevice(getInstrumentation(), Utils.ROTATION_MODE_NATURAL);
+ super.afterTest(metrics);
+ }
+
+ @JankTest(expectedFrames=100, defaultIterationCount=2)
+ @GfxMonitor(processName=Utils.PACKAGE)
+ public void testRotation() throws Exception {
+ for (int i = 0; i < STEP_CNT; ++i) {
+ Utils.rotateDevice(getInstrumentation(),
+ Utils.getDeviceRotation(getInstrumentation()) == Utils.ROTATION_MODE_LEFT ?
+ Utils.ROTATION_MODE_RIGHT : Utils.ROTATION_MODE_LEFT);
+ }
+ }
+}
diff --git a/tests/WindowAnimationJank/src/android/windowanimationjank/Utils.java b/tests/WindowAnimationJank/src/android/windowanimationjank/Utils.java
new file mode 100644
index 000000000000..25314644ca7e
--- /dev/null
+++ b/tests/WindowAnimationJank/src/android/windowanimationjank/Utils.java
@@ -0,0 +1,118 @@
+/*
+ * 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.windowanimationjank;
+
+import android.app.Instrumentation;
+import android.app.UiAutomation;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.SystemClock;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.BySelector;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+
+/**
+ * Set of helpers to manipulate test activities.
+ */
+public class Utils {
+ protected final static String PACKAGE = "android.windowanimationjank";
+ protected final static String ELEMENT_LAYOUT_ACTIVITY = "ElementLayoutActivity";
+ protected final static String ELEMENT_LAYOUT_CLASS = PACKAGE + "." + ELEMENT_LAYOUT_ACTIVITY;
+ protected final static long WAIT_FOR_ACTIVITY_TIMEOUT = 10000;
+ private static final BySelector ROOT_ELEMENT_LAYOUT = By.res(PACKAGE, "root_flow_layout");
+
+ private final static long ROTATION_ANIMATION_TIME_FULL_SCREEN_MS = 1000;
+
+ protected final static int ROTATION_MODE_NATURAL = 0;
+ protected final static int ROTATION_MODE_LEFT = 1;
+ protected final static int ROTATION_MODE_RIGHT = 2;
+
+ private static UiObject2 waitForActivity(Instrumentation instrumentation, BySelector selector) {
+ UiDevice device = UiDevice.getInstance(instrumentation);
+ UiObject2 window = device.wait(Until.findObject(selector), WAIT_FOR_ACTIVITY_TIMEOUT);
+ if (window == null) {
+ throw new RuntimeException(selector.toString() + " has not been started.");
+ }
+
+ // Get root object.
+ while (window.getParent() != null) {
+ window = window.getParent();
+ }
+ return window;
+ }
+
+ public static UiObject2 waitForElementLayout(Instrumentation instrumentation) {
+ return waitForActivity(instrumentation, ROOT_ELEMENT_LAYOUT);
+ }
+
+ /**
+ * Start and return activity with requested number of random elements.
+ */
+ public static UiObject2 startElementLayout(Instrumentation instrumentation, int numElements) {
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.setComponent(new ComponentName(PACKAGE, ELEMENT_LAYOUT_CLASS));
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(ElementLayoutActivity.NUM_ELEMENTS_KEY, numElements);
+ instrumentation.getTargetContext().startActivity(intent);
+ return waitForElementLayout(instrumentation);
+ }
+
+ public static int getDeviceRotation(Instrumentation instrumentation) {
+ try {
+ UiDevice device = UiDevice.getInstance(instrumentation);
+ switch (device.getDisplayRotation()) {
+ case UiAutomation.ROTATION_FREEZE_90:
+ return ROTATION_MODE_LEFT;
+ case UiAutomation.ROTATION_FREEZE_270:
+ return ROTATION_MODE_RIGHT;
+ case UiAutomation.ROTATION_FREEZE_0:
+ case UiAutomation.ROTATION_FREEZE_180:
+ return ROTATION_MODE_NATURAL;
+ }
+ } catch(Exception e) {
+ throw new RuntimeException();
+ }
+ throw new RuntimeException("Unsupported device rotation.");
+ }
+
+ public static void rotateDevice(Instrumentation instrumentation, int rotationMode) {
+ try {
+ UiDevice device = UiDevice.getInstance(instrumentation);
+ long startTime = System.currentTimeMillis();
+ switch (rotationMode) {
+ case ROTATION_MODE_NATURAL:
+ device.setOrientationNatural();
+ break;
+ case ROTATION_MODE_LEFT:
+ device.setOrientationLeft();
+ break;
+ case ROTATION_MODE_RIGHT:
+ device.setOrientationRight();
+ break;
+ default:
+ throw new RuntimeException("Unsupported rotation mode: " + rotationMode);
+ }
+
+ long toSleep = ROTATION_ANIMATION_TIME_FULL_SCREEN_MS -
+ (System.currentTimeMillis() - startTime);
+ if (toSleep > 0) {
+ SystemClock.sleep(toSleep);
+ }
+ } catch(Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/tests/WindowAnimationJank/src/android/windowanimationjank/WindowAnimationJankTestBase.java b/tests/WindowAnimationJank/src/android/windowanimationjank/WindowAnimationJankTestBase.java
new file mode 100644
index 000000000000..bf739fa8da07
--- /dev/null
+++ b/tests/WindowAnimationJank/src/android/windowanimationjank/WindowAnimationJankTestBase.java
@@ -0,0 +1,56 @@
+/*
+ * 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.windowanimationjank;
+
+import java.io.IOException;
+import java.util.StringTokenizer;
+
+import android.support.test.jank.JankTestBase;
+import android.support.test.uiautomator.UiDevice;
+
+/**
+ * This adds additional system level jank monitor and its result is merged with primary monitor
+ * used in test.
+ */
+public abstract class WindowAnimationJankTestBase extends JankTestBase {
+ private static final String TAG = "WindowAnimationJankTestBase";
+
+ protected WindowAnimationJankTestBase() {
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ // fix device orientation
+ getUiDevice().setOrientationNatural();
+
+ // Start from the home screen
+ getUiDevice().pressHome();
+ getUiDevice().waitForIdle();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ getUiDevice().unfreezeRotation();
+ super.tearDown();
+ }
+
+ protected UiDevice getUiDevice() {
+ return UiDevice.getInstance(getInstrumentation());
+ }
+}