diff options
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()); + } +} |